Compare commits

..

27 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek 990307c3da
Merge pull request #16803 from poettering/analyze-condition-rework
support missing conditions/asserts everywhere
2020-08-20 18:18:13 +02:00
Lennart Poettering cbed1dc8af mount-util: tweak how we find inaccessible device nodes
On new kernels (>= 5.8) unprivileged users may create the 0:0 character
device node. Which is great, as we can use that as inaccessible device
nodes if we run unprivileged. Hence, change how we find the right
inaccessible device inodes: when the user asks for a block device node,
but we have none, try the char device node first. If that doesn't exist,
fall back to the socket node as before.

This means that:

1. in the best case we'll return a node if the right device node type
2. otherwise we hopefully at least can return a device node if one asked
   for even if the type doesn't match (i.e. we return char instead of
   the requested block device node)
3. in the worst case (old kernels…) we'll return a socket node
2020-08-20 18:15:29 +02:00
Lennart Poettering 5b14956385
Merge pull request #16543 from poettering/nspawn-run-host
nspawn: /run/host/ tweaks
2020-08-20 16:20:05 +02:00
Lennart Poettering c1093c34d7 sd-bus: fix error handling on readv()
let's make sure we collect the right error code from errno, otherwise
we'll see EPERM (i.e. error 1) for all errors readv() returns (since it
returns -1 on error), including EAGAIN.

This is definitely backport material.

A fix-up for 3691bcf3c5.

Fixes: #16699
2020-08-20 14:14:36 +02:00
Lennart Poettering 476cfe626d core: remove support for ConditionNull=
The concept is flawed, and mostly useless. Let's finally remove it.

It has been deprecated since 90a2ec10f2 (6
years ago) and we started to warn since
55dadc5c57 (1.5 years ago).

Let's get rid of it altogether.
2020-08-20 14:01:25 +02:00
Lennart Poettering 4f55a5b0bf core: add missing conditions/asserts to unit file parsing 2020-08-20 13:56:14 +02:00
Lennart Poettering 625a164069 analyze: rework condition testing
Let's drop the private table and just use the generic concepts we have
in place already that make the same information available.

Fixes: #16781
2020-08-20 13:47:45 +02:00
Luca Boccassi 7489ccc350 coding style: document how to break a function declaration 2020-08-20 13:19:28 +02:00
Lennart Poettering 037857507a man: fix xml tags 2020-08-20 13:19:01 +02:00
Lennart Poettering 7b24e6e3fa
Merge pull request #16221 from bluca/show_microsec
systemctl: add --timestamp to change timestamp print format
2020-08-20 13:15:04 +02:00
Lennart Poettering 5d1e68b494 user-runtime-dir: deal gracefully with missing logind properties
Fixes: #16685
2020-08-20 13:12:02 +02:00
Zbigniew Jędrzejewski-Szmek ec673ad4ab
Merge pull request #16559 from benzea/benzea/memory-recursiveprot
mount-setup: Enable memory_recursiveprot for cgroup2
2020-08-20 13:05:07 +02:00
Zbigniew Jędrzejewski-Szmek 0cd9ccb654
Merge pull request #16677 from poettering/statx-mntid
make use of new kernel 5.8 statx() mount id/mountpoint APIs
2020-08-20 10:58:14 +02:00
Lennart Poettering 3242980582 core: create per-user inaccessible node from the service manager
Previously, we'd create them from user-runtime-dir@.service. That has
one benefit: since this service runs privileged, we can create the full
set of device nodes. It has one major drawback though: it security-wise
problematic to create files/directories in directories as privileged
user in directories owned by unprivileged users, since they can use
symlinks to redirect what we want to do. As a general rule we hence
avoid this logic: only unpriv code should populate unpriv directories.

Hence, let's move this code to an appropriate place in the service
manager. This means we lose the inaccessible block device node, but
since there's already a fallback in place, this shouldn't be too bad.
2020-08-20 10:18:02 +02:00
Lennart Poettering 00e64c6d06 doc: document what we now place in /run/host 2020-08-20 10:17:59 +02:00
Lennart Poettering 0f48ba7b84 nspawn: provide $container and $container_uuid in /run/host too
This has the major benefit that the entire payload of the container can
access these files there. Previously, we'd set them only as env vars,
but that meant only PID 1 could read them directly or other privileged
payload code with access to /run/1/environ.
2020-08-20 10:17:55 +02:00
Lennart Poettering 9fac502920 nspawn,pid1: pass "inaccessible" nodes from cntr mgr to pid1 payload via /run/host
Let's make /run/host the sole place we pass stuff from host to container
in and place the "inaccessible" nodes in /run/host too.

In contrast to the previous two commits this is a minor compat break, but
not a relevant one I think. Previously the container manager would place
these nodes in /run/systemd/inaccessible/ and that's where PID 1 in the
container would try to add them too when missing. Container manager and
PID 1 in the container would thus manage the same dir together.

With this change the container manager now passes an immutable directory
to the container and leaves /run/systemd entirely untouched, and managed
exclusively by PID 1 inside the container, which is nice to have clear
separation on who manages what.

In order to make sure systemd then usses the /run/host/inaccesible/
nodes this commit changes PID 1 to look for that dir and if it exists
will symlink it to /run/systemd/inaccessible.

Now, this will work fine if new nspawn and new pid 1 in the container
work together. as then the symlink is created and the difference between
the two dirs won't matter.

For the case where an old nspawn invokes a new PID 1: in this case
things work as they always worked: the dir is managed together.

For the case where different container manager invokes a new PID 1: in
this case the nodes aren't typically passed in, and PID 1 in the
container will try to create them and will likely fail partially (though
gracefully) when trying to create char/block device nodes. THis is fine
though as there are fallbacks in place for that case.

For the case where a new nspawn invokes an old PID1: this is were the
(minor) incompatibily happens: in this case new nspawn will place the
nodes in the /run/host/inaccessible/ subdir, but the PID 1 in the
container won't look for them there. Since the nodes are also not
pre-created in /run/systed/inaccessible/ PID 1 will try to create them
there as if a different container manager sets them up. This is of
course not sexy, but is not a total loss, since as mentioned fallbacks
are in place anyway. Hence I think it's OK to accept this minor
incompatibility.
2020-08-20 10:17:52 +02:00
Lennart Poettering e96ceabac9 nspawn: move $NOTIFY_SOCKET into /run/host/ too
The sd_notify() socket that nspawn binds that the payload can use to
talk to it was previously stored in /run/systemd/nspawn/notify, which is
weird (as in the previous commit) since this makes /run/systemd
something that is cooperatively maintained by systemd inside the
container and nspawn outside of it.

We now have a better place where container managers can put the stuff
they want to pass to the payload: /run/host/, hence let's make use of
that.

This is not a compat breakage, since the sd_notify() protocol is based
on the $NOTIFY_SOCKET env var, where we place the new socket path.
2020-08-20 10:17:48 +02:00
Lennart Poettering 5a27b39518 nspawn/machine: move mount propagation dir to /run/host/incoming
Previously we'd use a directory /run/systemd/nspawn/incoming for
accepting mounts to propagate from the host. This is a bit weird, since
we have a shared namespace: /run/systemd/ contains both stuff managed by
the surround nspawn as well as from the systemd inside.

We now have the /run/host/ hierarchy that has special stuff we want to
pass from host to container. Let's make use of that here, and move this
directory here too.

This is not a compat breakage, since the payload never interfaces with
that directory natively: it's only nspawn and machined that need to
agree on it.
2020-08-20 10:17:25 +02:00
Luca Boccassi 46ad9c5378 systemctl: add --timestamp to change timestamp print format
Timestamps for unit start/stop are recorded with microsecond granularity,
but status and show truncate to second granularity by default.
Add a --timestamp=pretty|us|utc option to allow including the microseconds
or to use the UTC TZ to all timestamps printed by systemctl.
2020-08-19 15:30:13 +01:00
Luca Boccassi 7b3eb5c97e basic/time-util: add function to format timestamps with different styles
Instead of a multiple fixed format helper functions, add an enum and
a single helper, so that it's easier to extend in the future.
2020-08-19 15:30:13 +01:00
Benjamin Berg 29bb3d7fc4 man: Improve MemoryMin=/MemoryLow= description
The description didn't really explain how the distribution mechanism
works exactly and the relationship of leaf and slice units.

Update the documentation and also explicitly explain the expected
behaviour as it is created by the memory_recursiveprot cgroup2 mount
option.
2020-08-19 11:17:02 +02:00
Benjamin Berg 56f47800d8 mount-setup: Enable memory_recursiveprot for cgroup2
When available, enable memory_recursiveprot. Realistically it always
makes sense to delegate MemoryLow= and MemoryMin= to all children of a
slice/unit.

The kernel option is not enabled by default as it might cause
regressions in some setups. However, it is the better default in
general, and it results in a more flexible and obvious behaviour.

The alternative to using this option would be for user's to also set
DefaultMemoryLow= on slices when assigning MemoryLow=. However, this
makes the effect of MemoryLow= on some children less obvious, as it
could result in a lower protection rather than increasing it.

From the kernel documentation:

  memory_recursiveprot

        Recursively apply memory.min and memory.low protection to
        entire subtrees, without requiring explicit downward
        propagation into leaf cgroups.  This allows protecting entire
        subtrees from one another, while retaining free competition
        within those subtrees.  This should have been the default
        behavior but is a mount-option to avoid regressing setups
        relying on the original semantics (e.g. specifying bogusly
        high 'bypass' protection values at higher tree levels).

This was added in kernel commit 8a931f801340c (mm: memcontrol:
recursive memory.low protection), which became available in 5.7 and was
subsequently fixed in kernel 5.7.7 (mm: memcontrol: handle div0 crash
race condition in memory.low).
2020-08-19 11:17:01 +02:00
Lennart Poettering 8609bb80dd update TODO 2020-08-19 10:08:33 +02:00
Lennart Poettering 5f10408044 mountpoint-util: use new kernel 5.8 statx() API for determining mount points
We finally have an explicit API for this in the kernel. It's great and
simple. Let's use it!
2020-08-19 10:08:29 +02:00
Lennart Poettering 69b3fa14cd mountpoint-util: use new kernel 5.8 statx() API for determining mnt_id
The kernel finally has a proper API to determine the mnt_id of a file.
Let's use it.

This adds support for the STATX_MNT_ID field of statx(), added in
kernel 5.8.
2020-08-19 10:08:24 +02:00
Lennart Poettering ffaf45e4f3 mountpoint-util: minor modernizations 2020-08-19 10:07:49 +02:00
43 changed files with 516 additions and 402 deletions

3
TODO
View File

@ -19,9 +19,6 @@ Features:
* nss-systemd: also synthesize shadow records for users/groups * nss-systemd: also synthesize shadow records for users/groups
* make use of the new statx mountid and rootmount fields in path_get_mnt_id()
and fd_is_mount_point()
* nspawn: move "incoming mount" directory to /run/host, move "inaccessible" * nspawn: move "incoming mount" directory to /run/host, move "inaccessible"
nodes to /run/host, move notify socket (for sd_notify() between payload and nodes to /run/host, move notify socket (for sd_notify() between payload and
container manager) container manager)

View File

@ -25,6 +25,17 @@ layout: default
note that emacs loads `.dir-locals.el` automatically, but vim needs to be note that emacs loads `.dir-locals.el` automatically, but vim needs to be
configured to load `.vimrc`, see that file for instructions. configured to load `.vimrc`, see that file for instructions.
- If you break a function declaration over multiple lines, do it like this:
```c
void some_function(
int foo,
bool bar,
char baz) {
int a, b, c;
```
- Try to write this: - Try to write this:
```c ```c

View File

@ -172,6 +172,13 @@ manager, please consider supporting the following interfaces.
unit they created for their container. That's private property of systemd, unit they created for their container. That's private property of systemd,
and no other code should modify it. and no other code should modify it.
6. systemd running inside the container can report when boot-up is complete
using the usual `sd_notify()` protocol that is also used when a service
wants to tell the service manager about readiness. A container manager can
set the `$NOTIFY_SOCKET` environment variable to a suitable socket path to
make use of this functionality. (Also see information about
`/run/host/notify` below.)
## Networking ## Networking
1. Inside of a container, if a `veth` link is named `host0`, `systemd-networkd` 1. Inside of a container, if a `veth` link is named `host0`, `systemd-networkd`
@ -189,6 +196,62 @@ manager, please consider supporting the following interfaces.
devices, for example hashed out of the container names. That way it is more devices, for example hashed out of the container names. That way it is more
likely that DHCP and IPv4LL will acquire stable addresses. likely that DHCP and IPv4LL will acquire stable addresses.
## The `/run/host/` Hierarchy
Container managers may place certain resources the manager wants to provide to
the container payload below the `/run/host/` hierarchy. This hierarchy should
be mostly immutable (possibly some subdirs might be writable, but the top-level
hierarchy — and probably most subdirs should be read-only to the
container). Note that this hierarchy is used by various container managers, and
care should be taken to avoid naming conflicts. `systemd` (and in particular
`systemd-nspawn`) use the hierarchy for the following resources:
1. The `/run/host/incoming/` directory mount point is configured for `MS_SLAVE`
mount propagation with the host, and is used as intermediary location for
mounts to establish in the container, for the implementation of `machinectl
bind`. Container payload should usually not directly interact with this
directory: it's used by code outside the container to insert mounts inside
it only, and is mostly an internal vehicle to achieve this. Other container
managers that want to implement similar functionality might consider using
the same directory.
2. The `/run/host/inaccessible/` directory may be set up by the container
manager to include six file nodes: `reg`, `dir`, `fifo`, `sock`, `chr`,
`blk`. These nodes correspond with the six types of file nodes Linux knows
(with the exceptions of symlinks). Each node should be of the specific type
and have an all zero access mode, i.e. be inaccessible. The two device node
types should have major and minor of zero (which are unallocated devices on
Linux). These nodes are used as mount source for implementing the
`InaccessiblePath=` setting of unit files, i.e. file nodes to mask this way
are overmounted with these "inaccessible" inodes, guaranteeing that the file
node type does not change this way but the nodes still become
inaccessible. Note that systemd when run as PID 1 in the container payload
will create these nodes on its own if not passed in by the container
manager. However, in that case it likely lacks the privileges to create the
character and block devices nodes (there all fallbacks for this case).
3. The `/run/host/notify` path is a good choice to place the `sd_notify()`
socket in, that may be used for the container's PID 1 to report to the
container manager when boot-up is complete. The path used for this doesn't
matter much as it is communicated via the `$NOTIFY_SOCKET` environment
variable, following the usual protocol for this, however it's suitable, and
recommended place for this socket in case ready notification is desired.
4. The `/run/host/os-release` file contains the `/etc/os-release` file of the
host, i.e. may be used by the container payload to gather limited
information about the host environment, on top of what `uname -a` reports.
5. The `/run/host/container-manager` file may be used to pass the same
information as the `$container` environment variable (see above), i.e. a
short string identifying the container manager implementation. This file
should be newline terminated. Passing this information via this file has the
benefit that payload code can easily access it, even when running
unprivileged without access to the container PID1's environment block.
6. The `/run/host/container-uuid` file may be used to pass the same information
as the `$container_uuid` environment variable (see above). This file should
be newline terminated.
## What You Shouldn't Do ## What You Shouldn't Do
1. Do not drop `CAP_MKNOD` from the container. `PrivateDevices=` is a commonly 1. Do not drop `CAP_MKNOD` from the container. `PrivateDevices=` is a commonly

View File

@ -2134,6 +2134,20 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--timestamp=</option></term>
<listitem>
<para>Takes one of <literal>pretty</literal> (the default),
<literal>us</literal>, <literal>µs</literal>, <literal>utc</literal>.
Changes the format of printed timestamps.
<literal>pretty</literal>: <literal>Day YYYY-MM-DD HH:MM:SS TZ</literal>
<literal>us</literal> or <literal>µs</literal>: <literal>Day YYYY-MM-DD HH:MM:SS.UUUUUU TZ</literal>
<literal>utc</literal>: <literal>Day YYYY-MM-DD HH:MM:SS UTC</literal></para>
<literal>us+utc</literal> or <literal>µs+utc</literal>: <literal>Day YYYY-MM-DD HH:MM:SS.UUUUUU UTC</literal>
</listitem>
</varlistentry>
<xi:include href="user-system-options.xml" xpointer="host" /> <xi:include href="user-system-options.xml" xpointer="host" />
<xi:include href="user-system-options.xml" xpointer="machine" /> <xi:include href="user-system-options.xml" xpointer="machine" />

View File

@ -261,53 +261,42 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>MemoryMin=<replaceable>bytes</replaceable></varname></term> <term><varname>MemoryMin=<replaceable>bytes</replaceable></varname>, <varname>MemoryLow=<replaceable>bytes</replaceable></varname></term>
<listitem> <listitem>
<para>Specify the memory usage protection of the executed processes in this unit. If the memory usages of <para>Specify the memory usage protection of the executed processes in this unit.
this unit and all its ancestors are below their minimum boundaries, this unit's memory won't be reclaimed.</para> When reclaiming memory, the unit is treated as if it was using less memory resulting in memory
to be preferentially reclaimed from unprotected units.
Using <varname>MemoryLow=</varname> results in a weaker protection where memory may still
be reclaimed to avoid invoking the OOM killer in case there is no other reclaimable memory.</para>
<para>
For a protection to be effective, it is generally required to set a corresponding
allocation on all ancestors, which is then distributed between children
(with the exception of the root slice).
Any <varname>MemoryMin=</varname> or <varname>MemoryLow=</varname> allocation that is not
explicitly distributed to specific children is used to create a shared protection for all children.
As this is a shared protection, the children will freely compete for the memory.</para>
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is <para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
percentage value may be specified, which is taken relative to the installed physical memory on the percentage value may be specified, which is taken relative to the installed physical memory on the
system. If assigned the special value <literal>infinity</literal>, all available memory is protected, which may be system. If assigned the special value <literal>infinity</literal>, all available memory is protected, which may be
useful in order to always inherit all of the protection afforded by ancestors. useful in order to always inherit all of the protection afforded by ancestors.
This controls the <literal>memory.min</literal> control group attribute. For details about this This controls the <literal>memory.min</literal> or <literal>memory.low</literal> control group attribute.
control group attribute, see <ulink For details about this control group attribute, see <ulink
url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para> url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
<para>This setting is supported only if the unified control group hierarchy is used and disables <para>This setting is supported only if the unified control group hierarchy is used and disables
<varname>MemoryLimit=</varname>.</para> <varname>MemoryLimit=</varname>.</para>
<para>Units may have their children use a default <literal>memory.min</literal> value by specifying <para>Units may have their children use a default <literal>memory.min</literal> or
<varname>DefaultMemoryMin=</varname>, which has the same semantics as <varname>MemoryMin=</varname>. This setting <literal>memory.low</literal> value by specifying <varname>DefaultMemoryMin=</varname> or
does not affect <literal>memory.min</literal> in the unit itself.</para> <varname>DefaultMemoryLow=</varname>, which has the same semantics as
</listitem> <varname>MemoryMin=</varname> and <varname>MemoryLow=</varname>.
</varlistentry> This setting does not affect <literal>memory.min</literal> or <literal>memory.low</literal>
in the unit itself.
<varlistentry> Using it to set a default child allocation is only useful on kernels older than 5.7,
<term><varname>MemoryLow=<replaceable>bytes</replaceable></varname></term> which do not support the <literal>memory_recursiveprot</literal> cgroup2 mount option.</para>
<listitem>
<para>Specify the best-effort memory usage protection of the executed processes in this unit. If the memory
usages of this unit and all its ancestors are below their low boundaries, this unit's memory won't be
reclaimed as long as memory can be reclaimed from unprotected units.</para>
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
percentage value may be specified, which is taken relative to the installed physical memory on the
system. If assigned the special value <literal>infinity</literal>, all available memory is protected, which may be
useful in order to always inherit all of the protection afforded by ancestors.
This controls the <literal>memory.low</literal> control group attribute. For details about this
control group attribute, see <ulink
url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
<para>This setting is supported only if the unified control group hierarchy is used and disables
<varname>MemoryLimit=</varname>.</para>
<para>Units may have their children use a default <literal>memory.low</literal> value by specifying
<varname>DefaultMemoryLow=</varname>, which has the same semantics as <varname>MemoryLow=</varname>. This setting
does not affect <literal>memory.low</literal> in the unit itself.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -1092,9 +1092,6 @@
<para>Except for <varname>ConditionPathIsSymbolicLink=</varname>, all path checks follow symlinks.</para> <para>Except for <varname>ConditionPathIsSymbolicLink=</varname>, all path checks follow symlinks.</para>
<variablelist class='unit-directives'> <variablelist class='unit-directives'>
<!-- We do not document ConditionNull= here, as it is not particularly useful and probably just
confusing. -->
<varlistentry> <varlistentry>
<term><varname>ConditionArchitecture=</varname></term> <term><varname>ConditionArchitecture=</varname></term>

View File

@ -127,7 +127,7 @@ _systemctl () {
--quiet -q --system --user --version --runtime --recursive -r --firmware-setup --quiet -q --system --user --version --runtime --recursive -r --firmware-setup
--show-types -i --ignore-inhibitors --plain --failed --value --fail --dry-run --wait' --show-types -i --ignore-inhibitors --plain --failed --value --fail --dry-run --wait'
[ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --job-mode --root [ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --job-mode --root
--preset-mode -n --lines -o --output -M --machine --message' --preset-mode -n --lines -o --output -M --machine --message --timestamp'
) )
if __contains_word "--user" ${COMP_WORDS[*]}; then if __contains_word "--user" ${COMP_WORDS[*]}; then
@ -176,6 +176,9 @@ _systemctl () {
--machine|-M) --machine|-M)
comps=$( __get_machines ) comps=$( __get_machines )
;; ;;
--timestamp)
comps='pretty us µs utc us+utc µs+utc'
;;
esac esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0 return 0

View File

@ -431,6 +431,13 @@ done
_values -s , "${_modes[@]}" _values -s , "${_modes[@]}"
} }
(( $+functions[_systemctl_timestamp] )) ||
_systemctl_timestamp() {
local -a _styles
_styles=(help pretty us µs utc us+utc µs+utc)
_values -s , "${_styles[@]}"
}
# Build arguments for "systemctl" to be used in completion. # Build arguments for "systemctl" to be used in completion.
local -a _modes; _modes=("--user" "--system") local -a _modes; _modes=("--user" "--system")
# Use the last mode (they are exclusive and the last one is used). # Use the last mode (they are exclusive and the last one is used).
@ -471,4 +478,5 @@ _arguments -s \
'--firmware-setup[Tell the firmware to show the setup menu on next boot]' \ '--firmware-setup[Tell the firmware to show the setup menu on next boot]' \
'--plain[When used with list-dependencies, print output as a list]' \ '--plain[When used with list-dependencies, print output as a list]' \
'--failed[Show failed units]' \ '--failed[Show failed units]' \
'--timestamp=[Change format of printed timestamps]:style:_systemctl_timestamp' \
'*::systemctl command:_systemctl_commands' '*::systemctl command:_systemctl_commands'

View File

@ -8,84 +8,28 @@
#include "load-fragment.h" #include "load-fragment.h"
#include "service.h" #include "service.h"
typedef struct condition_definition {
const char *name;
ConfigParserCallback parser;
ConditionType type;
} condition_definition;
static const condition_definition condition_definitions[] = {
{ "ConditionPathExists", config_parse_unit_condition_path, CONDITION_PATH_EXISTS },
{ "ConditionPathExistsGlob", config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB },
{ "ConditionPathIsDirectory", config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY },
{ "ConditionPathIsSymbolicLink", config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK },
{ "ConditionPathIsMountPoint", config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT },
{ "ConditionPathIsReadWrite", config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE },
{ "ConditionPathIsEncrypted", config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED },
{ "ConditionDirectoryNotEmpty", config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY },
{ "ConditionFileNotEmpty", config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY },
{ "ConditionFileIsExecutable", config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE },
{ "ConditionNeedsUpdate", config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE },
{ "ConditionFirstBoot", config_parse_unit_condition_string, CONDITION_FIRST_BOOT },
{ "ConditionKernelCommandLine", config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE },
{ "ConditionKernelVersion", config_parse_unit_condition_string, CONDITION_KERNEL_VERSION },
{ "ConditionArchitecture", config_parse_unit_condition_string, CONDITION_ARCHITECTURE },
{ "ConditionVirtualization", config_parse_unit_condition_string, CONDITION_VIRTUALIZATION },
{ "ConditionSecurity", config_parse_unit_condition_string, CONDITION_SECURITY },
{ "ConditionCapability", config_parse_unit_condition_string, CONDITION_CAPABILITY },
{ "ConditionHost", config_parse_unit_condition_string, CONDITION_HOST },
{ "ConditionACPower", config_parse_unit_condition_string, CONDITION_AC_POWER },
{ "ConditionUser", config_parse_unit_condition_string, CONDITION_USER },
{ "ConditionGroup", config_parse_unit_condition_string, CONDITION_GROUP },
{ "ConditionControlGroupController", config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER },
{ "AssertPathExists", config_parse_unit_condition_path, CONDITION_PATH_EXISTS },
{ "AssertPathExistsGlob", config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB },
{ "AssertPathIsDirectory", config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY },
{ "AssertPathIsSymbolicLink", config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK },
{ "AssertPathIsMountPoint", config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT },
{ "AssertPathIsReadWrite", config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE },
{ "AssertPathIsEncrypted", config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED },
{ "AssertDirectoryNotEmpty", config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY },
{ "AssertFileNotEmpty", config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY },
{ "AssertFileIsExecutable", config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE },
{ "AssertNeedsUpdate", config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE },
{ "AssertFirstBoot", config_parse_unit_condition_string, CONDITION_FIRST_BOOT },
{ "AssertKernelCommandLine", config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE },
{ "AssertKernelVersion", config_parse_unit_condition_string, CONDITION_KERNEL_VERSION },
{ "AssertArchitecture", config_parse_unit_condition_string, CONDITION_ARCHITECTURE },
{ "AssertVirtualization", config_parse_unit_condition_string, CONDITION_VIRTUALIZATION },
{ "AssertSecurity", config_parse_unit_condition_string, CONDITION_SECURITY },
{ "AssertCapability", config_parse_unit_condition_string, CONDITION_CAPABILITY },
{ "AssertHost", config_parse_unit_condition_string, CONDITION_HOST },
{ "AssertACPower", config_parse_unit_condition_string, CONDITION_AC_POWER },
{ "AssertUser", config_parse_unit_condition_string, CONDITION_USER },
{ "AssertGroup", config_parse_unit_condition_string, CONDITION_GROUP },
{ "AssertControlGroupController", config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER },
/* deprecated, but we should still parse them */
{ "ConditionNull", config_parse_unit_condition_null, 0 },
{ "AssertNull", config_parse_unit_condition_null, 0 },
};
static int parse_condition(Unit *u, const char *line) { static int parse_condition(Unit *u, const char *line) {
const char *p; assert(u);
assert(line);
for (ConditionType t = 0; t < _CONDITION_TYPE_MAX; t++) {
ConfigParserCallback callback;
Condition **target; Condition **target;
const char *p, *name;
if ((p = startswith(line, "Condition"))) name = condition_type_to_string(t);
p = startswith(line, name);
if (p)
target = &u->conditions; target = &u->conditions;
else if ((p = startswith(line, "Assert"))) else {
target = &u->asserts; name = assert_type_to_string(t);
else p = startswith(line, name);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot parse \"%s\".", line);
for (size_t i = 0; i < ELEMENTSOF(condition_definitions); i++) {
const condition_definition *c = &condition_definitions[i];
p = startswith(line, c->name);
if (!p) if (!p)
continue; continue;
target = &u->asserts;
}
p += strspn(p, WHITESPACE); p += strspn(p, WHITESPACE);
if (*p != '=') if (*p != '=')
@ -94,7 +38,12 @@ static int parse_condition(Unit *u, const char *line) {
p += strspn(p, WHITESPACE); p += strspn(p, WHITESPACE);
return c->parser(NULL, "(stdin)", 0, NULL, 0, c->name, c->type, p, target, u); if (condition_takes_path(t))
callback = config_parse_unit_condition_path;
else
callback = config_parse_unit_condition_string;
return callback(NULL, "(cmdline)", 0, NULL, 0, name, t, p, target, u);
} }
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot parse \"%s\".", line); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot parse \"%s\".", line);

View File

@ -8,38 +8,47 @@
#include <linux/stat.h> #include <linux/stat.h>
#endif #endif
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ /* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
#define STATX_DEFINITION { \
__u32 stx_mask; \
__u32 stx_blksize; \
__u64 stx_attributes; \
__u32 stx_nlink; \
__u32 stx_uid; \
__u32 stx_gid; \
__u16 stx_mode; \
__u16 __spare0[1]; \
__u64 stx_ino; \
__u64 stx_size; \
__u64 stx_blocks; \
__u64 stx_attributes_mask; \
struct statx_timestamp stx_atime; \
struct statx_timestamp stx_btime; \
struct statx_timestamp stx_ctime; \
struct statx_timestamp stx_mtime; \
__u32 stx_rdev_major; \
__u32 stx_rdev_minor; \
__u32 stx_dev_major; \
__u32 stx_dev_minor; \
__u64 stx_mnt_id; \
__u64 __spare2; \
__u64 __spare3[12]; \
}
#if !HAVE_STRUCT_STATX #if !HAVE_STRUCT_STATX
struct statx_timestamp { struct statx_timestamp {
__s64 tv_sec; __s64 tv_sec;
__u32 tv_nsec; __u32 tv_nsec;
__s32 __reserved; __s32 __reserved;
}; };
struct statx {
__u32 stx_mask; struct statx STATX_DEFINITION;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 stx_attributes_mask;
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
#endif #endif
/* Always define the newest version we are aware of as a distinct type, so that we can use it even if glibc
* defines an older definition */
struct new_statx STATX_DEFINITION;
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ /* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
#ifndef STATX_BTIME #ifndef STATX_BTIME
#define STATX_BTIME 0x00000800U #define STATX_BTIME 0x00000800U
@ -49,3 +58,13 @@ struct statx {
#ifndef AT_STATX_DONT_SYNC #ifndef AT_STATX_DONT_SYNC
#define AT_STATX_DONT_SYNC 0x4000 #define AT_STATX_DONT_SYNC 0x4000
#endif #endif
/* fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60 (5.8) */
#ifndef STATX_MNT_ID
#define STATX_MNT_ID 0x00001000U
#endif
/* 80340fe3605c0e78cfe496c3b3878be828cfdbfe (5.8) */
#ifndef STATX_ATTR_MOUNT_ROOT
#define STATX_ATTR_MOUNT_ROOT 0x00002000 /* Root of a mount */
#endif

View File

@ -482,7 +482,7 @@ static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flag
# endif # endif
} }
# define statx missing_statx # define statx(dfd, filename, flags, mask, buffer) missing_statx(dfd, filename, flags, mask, buffer)
#endif #endif
#if !HAVE_SET_MEMPOLICY #if !HAVE_SET_MEMPOLICY

View File

@ -8,6 +8,8 @@
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "fs-util.h" #include "fs-util.h"
#include "missing_stat.h"
#include "missing_syscall.h"
#include "mountpoint-util.h" #include "mountpoint-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
@ -32,6 +34,8 @@ int name_to_handle_at_loop(
_cleanup_free_ struct file_handle *h = NULL; _cleanup_free_ struct file_handle *h = NULL;
size_t n = ORIGINAL_MAX_HANDLE_SZ; size_t n = ORIGINAL_MAX_HANDLE_SZ;
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
/* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
* buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
* start value, it is not an upper bound on the buffer size required. * start value, it is not an upper bound on the buffer size required.
@ -86,13 +90,16 @@ int name_to_handle_at_loop(
} }
} }
static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) { static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mnt_id) {
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *fdinfo = NULL; _cleanup_free_ char *fdinfo = NULL;
_cleanup_close_ int subfd = -1; _cleanup_close_ int subfd = -1;
char *p; char *p;
int r; int r;
assert(ret_mnt_id);
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
if ((flags & AT_EMPTY_PATH) && isempty(filename)) if ((flags & AT_EMPTY_PATH) && isempty(filename))
xsprintf(path, "/proc/self/fdinfo/%i", fd); xsprintf(path, "/proc/self/fdinfo/%i", fd);
else { else {
@ -121,7 +128,7 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id
p += strspn(p, WHITESPACE); p += strspn(p, WHITESPACE);
p[strcspn(p, WHITESPACE)] = 0; p[strcspn(p, WHITESPACE)] = 0;
return safe_atoi(p, mnt_id); return safe_atoi(p, ret_mnt_id);
} }
int fd_is_mount_point(int fd, const char *filename, int flags) { int fd_is_mount_point(int fd, const char *filename, int flags) {
@ -129,33 +136,46 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
int mount_id = -1, mount_id_parent = -1; int mount_id = -1, mount_id_parent = -1;
bool nosupp = false, check_st_dev = true; bool nosupp = false, check_st_dev = true;
struct stat a, b; struct stat a, b;
struct statx sx
#if HAS_FEATURE_MEMORY_SANITIZER
= {}
# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
#endif
;
int r; int r;
assert(fd >= 0); assert(fd >= 0);
assert(filename); assert(filename);
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
/* First we will try the name_to_handle_at() syscall, which /* First we will try statx()' STATX_ATTR_MOUNT_ROOT attribute, which is our ideal API, available
* tells us the mount id and an opaque file "handle". It is * since kernel 5.8.
* not supported everywhere though (kernel compile-time
* option, not all file systems are hooked up). If it works
* the mount id is usually good enough to tell us whether
* something is a mount point.
* *
* If that didn't work we will try to read the mount id from * If that fails, our second try is the name_to_handle_at() syscall, which tells us the mount id and
* /proc/self/fdinfo/<fd>. This is almost as good as * an opaque file "handle". It is not supported everywhere though (kernel compile-time option, not
* name_to_handle_at(), however, does not return the * all file systems are hooked up). If it works the mount id is usually good enough to tell us
* opaque file handle. The opaque file handle is pretty useful * whether something is a mount point.
* to detect the root directory, which we should always *
* consider a mount point. Hence we use this only as * If that didn't work we will try to read the mount id from /proc/self/fdinfo/<fd>. This is almost
* fallback. Exporting the mnt_id in fdinfo is a pretty recent * as good as name_to_handle_at(), however, does not return the opaque file handle. The opaque file
* handle is pretty useful to detect the root directory, which we should always consider a mount
* point. Hence we use this only as fallback. Exporting the mnt_id in fdinfo is a pretty recent
* kernel addition. * kernel addition.
* *
* As last fallback we do traditional fstat() based st_dev * As last fallback we do traditional fstat() based st_dev comparisons. This is how things were
* comparisons. This is how things were traditionally done, * traditionally done, but unionfs breaks this since it exposes file systems with a variety of st_dev
* but unionfs breaks this since it exposes file * reported. Also, btrfs subvolumes have different st_dev, even though they aren't real mounts of
* systems with a variety of st_dev reported. Also, btrfs * their own. */
* subvolumes have different st_dev, even though they aren't
* real mounts of their own. */ if (statx(fd, filename, (FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW) |
(flags & AT_EMPTY_PATH) |
AT_NO_AUTOMOUNT, 0, &sx) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
/* If statx() is not available or forbidden, fallback to name_to_handle_at() below */
} else if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) /* yay! */
return FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
r = name_to_handle_at_loop(fd, filename, &h, &mount_id, flags); r = name_to_handle_at_loop(fd, filename, &h, &mount_id, flags);
if (IN_SET(r, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) if (IN_SET(r, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL))
@ -278,8 +298,29 @@ int path_is_mount_point(const char *t, const char *root, int flags) {
} }
int path_get_mnt_id(const char *path, int *ret) { int path_get_mnt_id(const char *path, int *ret) {
union {
struct statx sx;
struct new_statx nsx;
} buf
#if HAS_FEATURE_MEMORY_SANITIZER
= {}
# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
#endif
;
int r; int r;
if (statx(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_MNT_ID, &buf.sx) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
/* Fall back to name_to_handle_at() and then fdinfo if statx is not supported or we lack
* privileges */
} else if (FLAGS_SET(buf.nsx.stx_mask, STATX_MNT_ID)) {
*ret = buf.nsx.stx_mnt_id;
return 0;
}
r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0); r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0);
if (IN_SET(r, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) /* kernel/fs don't support this, or seccomp blocks access, or untriggered mount, or name_to_handle_at() is flaky */ if (IN_SET(r, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) /* kernel/fs don't support this, or seccomp blocks access, or untriggered mount, or name_to_handle_at() is flaky */
return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret); return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);

View File

@ -23,6 +23,7 @@
#include "path-util.h" #include "path-util.h"
#include "process-util.h" #include "process-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "time-util.h" #include "time-util.h"
@ -282,12 +283,11 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) {
return tv; return tv;
} }
static char *format_timestamp_internal( char *format_timestamp_style(
char *buf, char *buf,
size_t l, size_t l,
usec_t t, usec_t t,
bool utc, TimestampStyle style) {
bool us) {
/* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
* generated timestamps may be parsed with parse_timestamp(), and always read the same. */ * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
@ -304,9 +304,27 @@ static char *format_timestamp_internal(
struct tm tm; struct tm tm;
time_t sec; time_t sec;
size_t n; size_t n;
bool utc = false, us = false;
assert(buf); assert(buf);
switch (style) {
case TIMESTAMP_PRETTY:
break;
case TIMESTAMP_US:
us = true;
break;
case TIMESTAMP_UTC:
utc = true;
break;
case TIMESTAMP_US_UTC:
us = true;
utc = true;
break;
default:
return NULL;
}
if (l < (size_t) (3 + /* week day */ if (l < (size_t) (3 + /* week day */
1 + 10 + /* space and date */ 1 + 10 + /* space and date */
1 + 8 + /* space and time */ 1 + 8 + /* space and time */
@ -380,22 +398,6 @@ static char *format_timestamp_internal(
return buf; return buf;
} }
char *format_timestamp(char *buf, size_t l, usec_t t) {
return format_timestamp_internal(buf, l, t, false, false);
}
char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
return format_timestamp_internal(buf, l, t, true, false);
}
char *format_timestamp_us(char *buf, size_t l, usec_t t) {
return format_timestamp_internal(buf, l, t, false, true);
}
char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
return format_timestamp_internal(buf, l, t, true, true);
}
char *format_timestamp_relative(char *buf, size_t l, usec_t t) { char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
const char *s; const char *s;
usec_t n, d; usec_t n, d;
@ -1568,3 +1570,27 @@ int time_change_fd(void) {
return -errno; return -errno;
} }
static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = {
[TIMESTAMP_PRETTY] = "pretty",
[TIMESTAMP_US] = "us",
[TIMESTAMP_UTC] = "utc",
[TIMESTAMP_US_UTC] = "us+utc",
};
/* Use the macro for enum → string to allow for aliases */
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle,);
/* For the string → enum mapping we use the generic implementation, but also support two aliases */
TimestampStyle timestamp_style_from_string(const char *s) {
TimestampStyle t;
t = (TimestampStyle) string_table_lookup(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
if (t >= 0)
return t;
if (streq_ptr(s, "µs"))
return TIMESTAMP_US;
if (streq_ptr(s, "µs+uts"))
return TIMESTAMP_US_UTC;
return t;
}

View File

@ -29,6 +29,15 @@ typedef struct triple_timestamp {
usec_t boottime; usec_t boottime;
} triple_timestamp; } triple_timestamp;
typedef enum TimestampStyle {
TIMESTAMP_PRETTY,
TIMESTAMP_US,
TIMESTAMP_UTC,
TIMESTAMP_US_UTC,
_TIMESTAMP_STYLE_MAX,
_TIMESTAMP_STYLE_INVALID = -1,
} TimestampStyle;
#define USEC_INFINITY ((usec_t) UINT64_MAX) #define USEC_INFINITY ((usec_t) UINT64_MAX)
#define NSEC_INFINITY ((nsec_t) UINT64_MAX) #define NSEC_INFINITY ((nsec_t) UINT64_MAX)
@ -107,13 +116,14 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u);
usec_t timeval_load(const struct timeval *tv) _pure_; usec_t timeval_load(const struct timeval *tv) _pure_;
struct timeval *timeval_store(struct timeval *tv, usec_t u); struct timeval *timeval_store(struct timeval *tv, usec_t u);
char *format_timestamp(char *buf, size_t l, usec_t t); char *format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style);
char *format_timestamp_utc(char *buf, size_t l, usec_t t);
char *format_timestamp_us(char *buf, size_t l, usec_t t);
char *format_timestamp_us_utc(char *buf, size_t l, usec_t t);
char *format_timestamp_relative(char *buf, size_t l, usec_t t); char *format_timestamp_relative(char *buf, size_t l, usec_t t);
char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
static inline char *format_timestamp(char *buf, size_t l, usec_t t) {
return format_timestamp_style(buf, l, t, TIMESTAMP_PRETTY);
}
int parse_timestamp(const char *t, usec_t *usec); int parse_timestamp(const char *t, usec_t *usec);
int parse_sec(const char *t, usec_t *usec); int parse_sec(const char *t, usec_t *usec);
@ -185,3 +195,6 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
#endif #endif
int time_change_fd(void); int time_change_fd(void);
const char* timestamp_style_to_string(TimestampStyle t) _const_;
TimestampStyle timestamp_style_from_string(const char *s) _pure_;

View File

@ -491,6 +491,16 @@ int detect_container(void) {
} }
} }
/* The container manager might have placed this in the /run/host hierarchy for us, which is best
* because we can be consumed just like that, without special privileges. */
r = read_one_line_file("/run/host/container-manager", &m);
if (r > 0) {
e = m;
goto translate_name;
}
if (!IN_SET(r, -ENOENT, 0))
return log_debug_errno(r, "Failed to read /run/systemd/container: %m");
if (getpid_cached() == 1) { if (getpid_cached() == 1) {
/* If we are PID 1 we can just check our own environment variable, and that's authoritative. /* If we are PID 1 we can just check our own environment variable, and that's authoritative.
* We distinguish three cases: * We distinguish three cases:

View File

@ -1974,14 +1974,11 @@ static int bus_set_transient_conditions(
if (t < 0) if (t < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid condition type: %s", type_name); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid condition type: %s", type_name);
if (t != CONDITION_NULL) {
if (isempty(param)) if (isempty(param))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Condition parameter in %s is empty", type_name); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Condition parameter in %s is empty", type_name);
if (condition_takes_path(t) && !path_is_absolute(param)) if (condition_takes_path(t) && !path_is_absolute(param))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in condition %s is not absolute: %s", type_name, param); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in condition %s is not absolute: %s", type_name, param);
} else
param = NULL;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
Condition *c; Condition *c;
@ -1992,14 +1989,9 @@ static int bus_set_transient_conditions(
LIST_PREPEND(conditions, *list, c); LIST_PREPEND(conditions, *list, c);
if (t != CONDITION_NULL)
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name,
"%s=%s%s%s", type_name, "%s=%s%s%s", type_name,
trigger ? "|" : "", negate ? "!" : "", param); trigger ? "|" : "", negate ? "!" : "", param);
else
unit_write_settingf(u, flags, name,
"%s=%s%s", type_name,
trigger ? "|" : "", yes_no(!negate));
} }
empty = false; empty = false;

View File

@ -272,46 +272,52 @@ Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_P
Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, conditions) Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, conditions)
Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, conditions) Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, conditions)
Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, conditions) Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, conditions)
Unit.ConditionPathIsEncrypted, config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED, offsetof(Unit, conditions)
Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, conditions) Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, conditions)
Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, conditions) Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, conditions)
Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, conditions) Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, conditions)
Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions) Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions)
Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions) Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions)
Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions)
Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions)
Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions) Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions)
Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions) Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions)
Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions)
Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions)
Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions)
Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions) Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions)
Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions) Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions)
Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions)
Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions) Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions)
Unit.ConditionMemory, config_parse_unit_condition_string, CONDITION_MEMORY, offsetof(Unit, conditions)
Unit.ConditionCPUs, config_parse_unit_condition_string, CONDITION_CPUS, offsetof(Unit, conditions)
Unit.ConditionEnvironment, config_parse_unit_condition_string, CONDITION_ENVIRONMENT, offsetof(Unit, conditions)
Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions) Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions)
Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions) Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions)
Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions) Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions)
Unit.ConditionNull, config_parse_unit_condition_null, 0, offsetof(Unit, conditions)
Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts) Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts)
Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts) Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts)
Unit.AssertPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, asserts) Unit.AssertPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, asserts)
Unit.AssertPathIsSymbolicLink, config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, asserts) Unit.AssertPathIsSymbolicLink, config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, asserts)
Unit.AssertPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, asserts) Unit.AssertPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, asserts)
Unit.AssertPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, asserts) Unit.AssertPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, asserts)
Unit.AssertPathIsEncrypted, config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED, offsetof(Unit, asserts)
Unit.AssertDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, asserts) Unit.AssertDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, asserts)
Unit.AssertFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, asserts) Unit.AssertFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, asserts)
Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, asserts) Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, asserts)
Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts) Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts)
Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts) Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts)
Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts)
Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts)
Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts) Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts)
Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts) Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts)
Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts)
Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts)
Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts)
Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts) Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts)
Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts) Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts)
Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts)
Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts) Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts)
Unit.AssertMemory, config_parse_unit_condition_string, CONDITION_MEMORY, offsetof(Unit, asserts)
Unit.AssertCPUs, config_parse_unit_condition_string, CONDITION_CPUS, offsetof(Unit, asserts)
Unit.AssertEnvironment, config_parse_unit_condition_string, CONDITION_ENVIRONMENT, offsetof(Unit, asserts)
Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts) Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts)
Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts) Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts)
Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts) Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts)
Unit.AssertNull, config_parse_unit_condition_null, 0, offsetof(Unit, asserts)
Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode) Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode)
m4_dnl m4_dnl
Service.PIDFile, config_parse_pid_file, 0, offsetof(Service, pid_file) Service.PIDFile, config_parse_pid_file, 0, offsetof(Service, pid_file)

View File

@ -2999,60 +2999,6 @@ int config_parse_unit_condition_string(
return 0; return 0;
} }
int config_parse_unit_condition_null(
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) {
Condition **list = data, *c;
bool trigger, negate;
int b;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is deprecated, please do not use.", lvalue);
if (isempty(rvalue)) {
/* Empty assignment resets the list */
*list = condition_free_list(*list);
return 0;
}
trigger = rvalue[0] == '|';
if (trigger)
rvalue++;
negate = rvalue[0] == '!';
if (negate)
rvalue++;
b = parse_boolean(rvalue);
if (b < 0) {
log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
return 0;
}
if (!b)
negate = !negate;
c = condition_new(CONDITION_NULL, NULL, trigger, negate);
if (!c)
return log_oom();
LIST_PREPEND(conditions, *list, c);
return 0;
}
int config_parse_unit_requires_mounts_for( int config_parse_unit_requires_mounts_for(
const char *unit, const char *unit,
const char *filename, const char *filename,
@ -5266,7 +5212,6 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_ip_tos, "TOS" }, { config_parse_ip_tos, "TOS" },
{ config_parse_unit_condition_path, "CONDITION" }, { config_parse_unit_condition_path, "CONDITION" },
{ config_parse_unit_condition_string, "CONDITION" }, { config_parse_unit_condition_string, "CONDITION" },
{ config_parse_unit_condition_null, "CONDITION" },
{ config_parse_unit_slice, "SLICE" }, { config_parse_unit_slice, "SLICE" },
{ config_parse_documentation, "URL" }, { config_parse_documentation, "URL" },
{ config_parse_service_timeout, "SECONDS" }, { config_parse_service_timeout, "SECONDS" },

View File

@ -58,7 +58,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_unit_env_file);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_tos); CONFIG_PARSER_PROTOTYPE(config_parse_ip_tos);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_path); CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_path);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_string); CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_string);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_null);
CONFIG_PARSER_PROTOTYPE(config_parse_kill_mode); CONFIG_PARSER_PROTOTYPE(config_parse_kill_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_notify_access); CONFIG_PARSER_PROTOTYPE(config_parse_notify_access);
CONFIG_PARSER_PROTOTYPE(config_parse_emergency_action); CONFIG_PARSER_PROTOTYPE(config_parse_emergency_action);

View File

@ -32,6 +32,7 @@
#include "dbus-manager.h" #include "dbus-manager.h"
#include "dbus.h" #include "dbus.h"
#include "def.h" #include "def.h"
#include "dev-setup.h"
#include "efi-random.h" #include "efi-random.h"
#include "efivars.h" #include "efivars.h"
#include "emergency-action.h" #include "emergency-action.h"
@ -53,6 +54,7 @@
#include "loopback-setup.h" #include "loopback-setup.h"
#include "machine-id-setup.h" #include "machine-id-setup.h"
#include "manager.h" #include "manager.h"
#include "mkdir.h"
#include "mount-setup.h" #include "mount-setup.h"
#include "os-util.h" #include "os-util.h"
#include "pager.h" #include "pager.h"
@ -2073,6 +2075,20 @@ static int initialize_runtime(
if (r < 0) if (r < 0)
log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device); log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device);
} }
} else {
_cleanup_free_ char *p = NULL;
/* Create the runtime directory and place the inaccessible device nodes there, if we run in
* user mode. In system mode mount_setup() already did that. */
r = xdg_user_runtime_dir(&p, "/systemd");
if (r < 0) {
*ret_error_message = "$XDG_RUNTIME_DIR is not set";
return log_emergency_errno(r, "Failed to determine $XDG_RUNTIME_DIR path: %m");
}
(void) mkdir_p(p, 0755);
(void) make_inaccessible_nodes(p, UID_INVALID, GID_INVALID);
} }
if (arg_timer_slack_nsec != NSEC_INFINITY) if (arg_timer_slack_nsec != NSEC_INFINITY)

View File

@ -85,6 +85,8 @@ static const MountPoint mount_table[] = {
#endif #endif
{ "tmpfs", "/run", "tmpfs", "mode=755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME, { "tmpfs", "/run", "tmpfs", "mode=755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER }, NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate,memory_recursiveprot", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
{ "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV, { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
{ "cgroup2", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, { "cgroup2", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
@ -536,8 +538,17 @@ int mount_setup(bool loaded_policy, bool leave_propagation) {
(void) mkdir_label("/run/systemd/system", 0755); (void) mkdir_label("/run/systemd/system", 0755);
/* Also create /run/systemd/inaccessible nodes, so that we always have something to mount /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount
* inaccessible nodes from. */ * inaccessible nodes from. If we run in a container the host might have created these for us already
(void) make_inaccessible_nodes(NULL, UID_INVALID, GID_INVALID); * in /run/host/inaccessible/. Use those if we can, since tht way we likely get access to block/char
* device nodes that are inaccessible, and if userns is used to nodes that are on mounts owned by a
* userns outside the container and thus nicely read-only and not remountable. */
if (access("/run/host/inaccessible/", F_OK) < 0) {
if (errno != ENOENT)
log_debug_errno(errno, "Failed to check if /run/host/inaccessible exists, ignoring: %m");
(void) make_inaccessible_nodes("/run/systemd", UID_INVALID, GID_INVALID);
} else
(void) symlink("../host/inaccessible", "/run/systemd/inaccessible");
return 0; return 0;
} }

View File

@ -265,7 +265,7 @@ get_parent:
static char *format_timestamp_maybe_utc(char *buf, size_t l, usec_t t) { static char *format_timestamp_maybe_utc(char *buf, size_t l, usec_t t) {
if (arg_utc) if (arg_utc)
return format_timestamp_utc(buf, l, t); return format_timestamp_style(buf, l, t, TIMESTAMP_UTC);
return format_timestamp(buf, l, t); return format_timestamp(buf, l, t);
} }

View File

@ -544,9 +544,11 @@ static int bus_socket_read_auth(sd_bus *b) {
iov = IOVEC_MAKE((uint8_t *)b->rbuffer + b->rbuffer_size, n - b->rbuffer_size); iov = IOVEC_MAKE((uint8_t *)b->rbuffer + b->rbuffer_size, n - b->rbuffer_size);
if (b->prefer_readv) if (b->prefer_readv) {
k = readv(b->input_fd, &iov, 1); k = readv(b->input_fd, &iov, 1);
else { if (k < 0)
k = -errno;
} else {
mh = (struct msghdr) { mh = (struct msghdr) {
.msg_iov = &iov, .msg_iov = &iov,
.msg_iovlen = 1, .msg_iovlen = 1,
@ -1187,9 +1189,11 @@ int bus_socket_read_message(sd_bus *bus) {
iov = IOVEC_MAKE((uint8_t *)bus->rbuffer + bus->rbuffer_size, need - bus->rbuffer_size); iov = IOVEC_MAKE((uint8_t *)bus->rbuffer + bus->rbuffer_size, need - bus->rbuffer_size);
if (bus->prefer_readv) if (bus->prefer_readv) {
k = readv(bus->input_fd, &iov, 1); k = readv(bus->input_fd, &iov, 1);
else { if (k < 0)
k = -errno;
} else {
mh = (struct msghdr) { mh = (struct msghdr) {
.msg_iov = &iov, .msg_iov = &iov,
.msg_iovlen = 1, .msg_iovlen = 1,

View File

@ -7,9 +7,10 @@
#include "bus-error.h" #include "bus-error.h"
#include "dev-setup.h" #include "dev-setup.h"
#include "fs-util.h"
#include "format-util.h" #include "format-util.h"
#include "fs-util.h"
#include "label.h" #include "label.h"
#include "limits-util.h"
#include "main-func.h" #include "main-func.h"
#include "mkdir.h" #include "mkdir.h"
#include "mountpoint-util.h" #include "mountpoint-util.h"
@ -32,12 +33,16 @@ static int acquire_runtime_dir_properties(uint64_t *size, uint64_t *inodes) {
return log_error_errno(r, "Failed to connect to system bus: %m"); return log_error_errno(r, "Failed to connect to system bus: %m");
r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', size); r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', size);
if (r < 0) if (r < 0) {
return log_error_errno(r, "Failed to acquire runtime directory size: %s", bus_error_message(&error, r)); log_warning_errno(r, "Failed to acquire runtime directory size, ignoring: %s", bus_error_message(&error, r));
*size = physical_memory_scale(10U, 100U); /* 10% */
}
r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectoryInodesMax", &error, 't', inodes); r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectoryInodesMax", &error, 't', inodes);
if (r < 0) if (r < 0) {
return log_error_errno(r, "Failed to acquire number of inodes for runtime directory: %s", bus_error_message(&error, r)); log_warning_errno(r, "Failed to acquire number of inodes for runtime directory, ignoring: %s", bus_error_message(&error, r));
*inodes = DIV_ROUND_UP(*size, 4096);
}
return 0; return 0;
} }
@ -83,7 +88,8 @@ static int user_mkdir_runtime_path(
goto fail; goto fail;
} }
log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s.\n" log_debug_errno(errno,
"Failed to mount per-user tmpfs directory %s.\n"
"Assuming containerized execution, ignoring: %m", runtime_path); "Assuming containerized execution, ignoring: %m", runtime_path);
r = chmod_and_chown(runtime_path, 0700, uid, gid); r = chmod_and_chown(runtime_path, 0700, uid, gid);
@ -98,8 +104,6 @@ static int user_mkdir_runtime_path(
log_warning_errno(r, "Failed to fix label of \"%s\", ignoring: %m", runtime_path); log_warning_errno(r, "Failed to fix label of \"%s\", ignoring: %m", runtime_path);
} }
/* Set up inaccessible nodes now so they're available if we decide to use them with user namespaces. */
(void) make_inaccessible_nodes(runtime_path, uid, gid);
return 0; return 0;
fail: fail:

View File

@ -978,9 +978,8 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
goto finish; goto finish;
} }
if (r == 0) { if (r == 0) {
const char *mount_inside; const char *mount_inside, *q;
int mntfd; int mntfd;
const char *q;
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
@ -1001,12 +1000,11 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
(void) mkdir_p(dest, 0755); (void) mkdir_p(dest, 0755);
else { else {
(void) mkdir_parents(dest, 0755); (void) mkdir_parents(dest, 0755);
safe_close(open(dest, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600)); (void) mknod(dest, S_IFREG|0600, 0);
} }
} }
/* Fifth, move the mount to the right place inside */ mount_inside = strjoina("/run/host/incoming/", basename(mount_outside));
mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside));
if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) { if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
r = log_error_errno(errno, "Failed to mount: %m"); r = log_error_errno(errno, "Failed to mount: %m");
goto child_fail; goto child_fail;

View File

@ -101,10 +101,8 @@
#include "user-util.h" #include "user-util.h"
#include "util.h" #include "util.h"
/* nspawn is listening on the socket at the path in the constant nspawn_notify_socket_path /* The notify socket inside the container it can use to talk to nspawn using the sd_notify(3) protocol */
* nspawn_notify_socket_path is relative to the container #define NSPAWN_NOTIFY_SOCKET_PATH "/run/host/notify"
* the init process in the container pid can send messages to nspawn following the sd_notify(3) protocol */
#define NSPAWN_NOTIFY_SOCKET_PATH "/run/systemd/nspawn/notify"
#define EXIT_FORCE_RESTART 133 #define EXIT_FORCE_RESTART 133
@ -2517,19 +2515,15 @@ static int setup_propagate(const char *root) {
p = strjoina("/run/systemd/nspawn/propagate/", arg_machine); p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
(void) mkdir_p(p, 0600); (void) mkdir_p(p, 0600);
r = userns_mkdir(root, "/run/systemd", 0755, 0, 0); r = userns_mkdir(root, "/run/host", 0755, 0, 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create /run/systemd: %m"); return log_error_errno(r, "Failed to create /run/host: %m");
r = userns_mkdir(root, "/run/systemd/nspawn", 0755, 0, 0); r = userns_mkdir(root, "/run/host/incoming", 0600, 0, 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create /run/systemd/nspawn: %m"); return log_error_errno(r, "Failed to create /run/host/incoming: %m");
r = userns_mkdir(root, "/run/systemd/nspawn/incoming", 0600, 0, 0); q = prefix_roota(root, "/run/host/incoming");
if (r < 0)
return log_error_errno(r, "Failed to create /run/systemd/nspawn/incoming: %m");
q = prefix_roota(root, "/run/systemd/nspawn/incoming");
r = mount_verbose(LOG_ERR, p, q, NULL, MS_BIND, NULL); r = mount_verbose(LOG_ERR, p, q, NULL, MS_BIND, NULL);
if (r < 0) if (r < 0)
return r; return r;
@ -2538,8 +2532,7 @@ static int setup_propagate(const char *root) {
if (r < 0) if (r < 0)
return r; return r;
/* machined will MS_MOVE into that directory, and that's only /* machined will MS_MOVE into that directory, and that's only supported for non-shared mounts. */
* supported for non-shared mounts. */
return mount_verbose(LOG_ERR, NULL, q, NULL, MS_SLAVE, NULL); return mount_verbose(LOG_ERR, NULL, q, NULL, MS_SLAVE, NULL);
} }
@ -3278,7 +3271,7 @@ static int inner_child(
return log_error_errno(errno, "execv(%s) failed: %m", exec_target); return log_error_errno(errno, "execv(%s) failed: %m", exec_target);
} }
static int setup_sd_notify_child(void) { static int setup_notify_child(void) {
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
union sockaddr_union sa = { union sockaddr_union sa = {
.un.sun_family = AF_UNIX, .un.sun_family = AF_UNIX,
@ -3530,7 +3523,7 @@ static int outer_child(
(void) dev_setup(directory, arg_uid_shift, arg_uid_shift); (void) dev_setup(directory, arg_uid_shift, arg_uid_shift);
p = prefix_roota(directory, "/run"); p = prefix_roota(directory, "/run/host");
(void) make_inaccessible_nodes(p, arg_uid_shift, arg_uid_shift); (void) make_inaccessible_nodes(p, arg_uid_shift, arg_uid_shift);
r = setup_pts(directory); r = setup_pts(directory);
@ -3571,6 +3564,14 @@ static int outer_child(
if (r < 0) if (r < 0)
return r; return r;
/* The same stuff as the $container env var, but nicely readable for the entire payload */
p = prefix_roota(directory, "/run/host/container-manager");
(void) write_string_file(p, arg_container_service_name, WRITE_STRING_FILE_CREATE);
/* The same stuff as the $container_uuid env var */
p = prefix_roota(directory, "/run/host/container-uuid");
(void) write_string_filef(p, WRITE_STRING_FILE_CREATE, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(arg_uuid));
if (!arg_use_cgns) { if (!arg_use_cgns) {
r = mount_cgroups( r = mount_cgroups(
directory, directory,
@ -3588,7 +3589,7 @@ static int outer_child(
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to move root directory: %m"); return log_error_errno(r, "Failed to move root directory: %m");
fd = setup_sd_notify_child(); fd = setup_notify_child();
if (fd < 0) if (fd < 0)
return fd; return fd;
@ -3801,7 +3802,7 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r
return 0; return 0;
} }
static int setup_sd_notify_parent(sd_event *event, int fd, pid_t *inner_child_pid, sd_event_source **notify_event_source) { static int setup_notify_parent(sd_event *event, int fd, pid_t *inner_child_pid, sd_event_source **notify_event_source) {
int r; int r;
r = sd_event_add_io(event, notify_event_source, fd, EPOLLIN, nspawn_dispatch_notify_fd, inner_child_pid); r = sd_event_add_io(event, notify_event_source, fd, EPOLLIN, nspawn_dispatch_notify_fd, inner_child_pid);
@ -4632,7 +4633,7 @@ static int run_container(
return log_error_errno(r, "Failed to attach bus to event loop: %m"); return log_error_errno(r, "Failed to attach bus to event loop: %m");
} }
r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(*pid), &notify_event_source); r = setup_notify_parent(event, notify_socket, PID_TO_PTR(*pid), &notify_event_source);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -52,7 +52,7 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
assert(type >= 0); assert(type >= 0);
assert(type < _CONDITION_TYPE_MAX); assert(type < _CONDITION_TYPE_MAX);
assert((!parameter) == (type == CONDITION_NULL)); assert(parameter);
c = new(Condition, 1); c = new(Condition, 1);
if (!c) if (!c)
@ -776,15 +776,6 @@ static int condition_test_file_is_executable(Condition *c, char **env) {
(st.st_mode & 0111)); (st.st_mode & 0111));
} }
static int condition_test_null(Condition *c, char **env) {
assert(c);
assert(c->type == CONDITION_NULL);
/* Note that during parsing we already evaluate the string and
* store it in c->negate */
return true;
}
int condition_test(Condition *c, char **env) { int condition_test(Condition *c, char **env) {
static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c, char **env) = { static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c, char **env) = {
@ -811,7 +802,6 @@ int condition_test(Condition *c, char **env) {
[CONDITION_USER] = condition_test_user, [CONDITION_USER] = condition_test_user,
[CONDITION_GROUP] = condition_test_group, [CONDITION_GROUP] = condition_test_group,
[CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller, [CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller,
[CONDITION_NULL] = condition_test_null,
[CONDITION_CPUS] = condition_test_cpus, [CONDITION_CPUS] = condition_test_cpus,
[CONDITION_MEMORY] = condition_test_memory, [CONDITION_MEMORY] = condition_test_memory,
[CONDITION_ENVIRONMENT] = condition_test_environment, [CONDITION_ENVIRONMENT] = condition_test_environment,
@ -859,23 +849,20 @@ bool condition_test_list(
r = condition_test(c, env); r = condition_test(c, env);
if (logger) { if (logger) {
const char *p = c->type == CONDITION_NULL ? "true" : c->parameter;
assert(p);
if (r < 0) if (r < 0)
logger(userdata, LOG_WARNING, r, PROJECT_FILE, __LINE__, __func__, logger(userdata, LOG_WARNING, r, PROJECT_FILE, __LINE__, __func__,
"Couldn't determine result for %s=%s%s%s, assuming failed: %m", "Couldn't determine result for %s=%s%s%s, assuming failed: %m",
to_string(c->type), to_string(c->type),
c->trigger ? "|" : "", c->trigger ? "|" : "",
c->negate ? "!" : "", c->negate ? "!" : "",
p); c->parameter);
else else
logger(userdata, LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__, logger(userdata, LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__,
"%s=%s%s%s %s.", "%s=%s%s%s %s.",
to_string(c->type), to_string(c->type),
c->trigger ? "|" : "", c->trigger ? "|" : "",
c->negate ? "!" : "", c->negate ? "!" : "",
p, c->parameter,
condition_result_to_string(c->result)); condition_result_to_string(c->result));
} }
@ -937,7 +924,6 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_USER] = "ConditionUser", [CONDITION_USER] = "ConditionUser",
[CONDITION_GROUP] = "ConditionGroup", [CONDITION_GROUP] = "ConditionGroup",
[CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController", [CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController",
[CONDITION_NULL] = "ConditionNull",
[CONDITION_CPUS] = "ConditionCPUs", [CONDITION_CPUS] = "ConditionCPUs",
[CONDITION_MEMORY] = "ConditionMemory", [CONDITION_MEMORY] = "ConditionMemory",
[CONDITION_ENVIRONMENT] = "ConditionEnvironment", [CONDITION_ENVIRONMENT] = "ConditionEnvironment",
@ -969,7 +955,6 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_USER] = "AssertUser", [CONDITION_USER] = "AssertUser",
[CONDITION_GROUP] = "AssertGroup", [CONDITION_GROUP] = "AssertGroup",
[CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController", [CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController",
[CONDITION_NULL] = "AssertNull",
[CONDITION_CPUS] = "AssertCPUs", [CONDITION_CPUS] = "AssertCPUs",
[CONDITION_MEMORY] = "AssertMemory", [CONDITION_MEMORY] = "AssertMemory",
[CONDITION_ENVIRONMENT] = "AssertEnvironment", [CONDITION_ENVIRONMENT] = "AssertEnvironment",

View File

@ -34,8 +34,6 @@ typedef enum ConditionType {
CONDITION_FILE_NOT_EMPTY, CONDITION_FILE_NOT_EMPTY,
CONDITION_FILE_IS_EXECUTABLE, CONDITION_FILE_IS_EXECUTABLE,
CONDITION_NULL,
CONDITION_USER, CONDITION_USER,
CONDITION_GROUP, CONDITION_GROUP,

View File

@ -57,7 +57,7 @@ int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
} }
int make_inaccessible_nodes( int make_inaccessible_nodes(
const char *runtime_dir, const char *parent_dir,
uid_t uid, uid_t uid,
gid_t gid) { gid_t gid) {
@ -65,28 +65,26 @@ int make_inaccessible_nodes(
const char *name; const char *name;
mode_t mode; mode_t mode;
} table[] = { } table[] = {
{ "/systemd", S_IFDIR | 0755 }, { "inaccessible", S_IFDIR | 0755 },
{ "/systemd/inaccessible", S_IFDIR | 0000 }, { "inaccessible/reg", S_IFREG | 0000 },
{ "/systemd/inaccessible/reg", S_IFREG | 0000 }, { "inaccessible/dir", S_IFDIR | 0000 },
{ "/systemd/inaccessible/dir", S_IFDIR | 0000 }, { "inaccessible/fifo", S_IFIFO | 0000 },
{ "/systemd/inaccessible/fifo", S_IFIFO | 0000 }, { "inaccessible/sock", S_IFSOCK | 0000 },
{ "/systemd/inaccessible/sock", S_IFSOCK | 0000 },
/* The following two are likely to fail if we lack the privs for it (for example in an userns /* The following two are likely to fail if we lack the privs for it (for example in an userns
* environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0 * environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0
* device nodes to be created). But that's entirely fine. Consumers of these files should carry * device nodes to be created). But that's entirely fine. Consumers of these files should carry
* fallback to use a different node then, for example <root>/inaccessible/sock, which is close * fallback to use a different node then, for example <root>/inaccessible/sock, which is close
* enough in behaviour and semantics for most uses. */ * enough in behaviour and semantics for most uses. */
{ "/systemd/inaccessible/chr", S_IFCHR | 0000 }, { "inaccessible/chr", S_IFCHR | 0000 },
{ "/systemd/inaccessible/blk", S_IFBLK | 0000 }, { "inaccessible/blk", S_IFBLK | 0000 },
}; };
_cleanup_umask_ mode_t u; _cleanup_umask_ mode_t u;
size_t i;
int r; int r;
if (!runtime_dir) if (!parent_dir)
runtime_dir = "/run"; parent_dir = "/run/systemd";
u = umask(0000); u = umask(0000);
@ -95,10 +93,10 @@ int make_inaccessible_nodes(
* to lock down these nodes as much as we can, but otherwise try to match them as closely as possible with the * to lock down these nodes as much as we can, but otherwise try to match them as closely as possible with the
* underlying file, i.e. in the best case we offer the same node type as the underlying node. */ * underlying file, i.e. in the best case we offer the same node type as the underlying node. */
for (i = 0; i < ELEMENTSOF(table); i++) { for (size_t i = 0; i < ELEMENTSOF(table); i++) {
_cleanup_free_ char *path = NULL; _cleanup_free_ char *path = NULL;
path = path_join(runtime_dir, table[i].name); path = path_join(parent_dir, table[i].name);
if (!path) if (!path)
return log_oom(); return log_oom();
@ -107,7 +105,6 @@ int make_inaccessible_nodes(
else else
r = mknod_label(path, table[i].mode, makedev(0, 0)); r = mknod_label(path, table[i].mode, makedev(0, 0));
if (r < 0) { if (r < 0) {
if (r != -EEXIST)
log_debug_errno(r, "Failed to create '%s', ignoring: %m", path); log_debug_errno(r, "Failed to create '%s', ignoring: %m", path);
continue; continue;
} }

View File

@ -5,4 +5,4 @@
int dev_setup(const char *prefix, uid_t uid, gid_t gid); int dev_setup(const char *prefix, uid_t uid, gid_t gid);
int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid); int make_inaccessible_nodes(const char *parent_dir, uid_t uid, gid_t gid);

View File

@ -1335,7 +1335,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
if (d->type == TABLE_TIMESTAMP) if (d->type == TABLE_TIMESTAMP)
ret = format_timestamp(p, FORMAT_TIMESTAMP_MAX, d->timestamp); ret = format_timestamp(p, FORMAT_TIMESTAMP_MAX, d->timestamp);
else if (d->type == TABLE_TIMESTAMP_UTC) else if (d->type == TABLE_TIMESTAMP_UTC)
ret = format_timestamp_utc(p, FORMAT_TIMESTAMP_MAX, d->timestamp); ret = format_timestamp_style(p, FORMAT_TIMESTAMP_MAX, d->timestamp, TIMESTAMP_UTC);
else else
ret = format_timestamp_relative(p, FORMAT_TIMESTAMP_MAX, d->timestamp); ret = format_timestamp_relative(p, FORMAT_TIMESTAMP_MAX, d->timestamp);
if (!ret) if (!ret)

View File

@ -368,7 +368,7 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
const char *k; const char *k;
if (flags & OUTPUT_UTC) if (flags & OUTPUT_UTC)
k = format_timestamp_utc(buf, sizeof(buf), x); k = format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_UTC);
else else
k = format_timestamp(buf, sizeof(buf), x); k = format_timestamp(buf, sizeof(buf), x);
if (!k) if (!k)
@ -685,8 +685,8 @@ static int output_verbose(
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to get cursor: %m"); return log_error_errno(r, "Failed to get cursor: %m");
timestamp = flags & OUTPUT_UTC ? format_timestamp_us_utc(ts, sizeof ts, realtime) timestamp = format_timestamp_style(ts, sizeof ts, realtime,
: format_timestamp_us(ts, sizeof ts, realtime); flags & OUTPUT_UTC ? TIMESTAMP_US_UTC : TIMESTAMP_US);
fprintf(f, "%s [%s]\n", fprintf(f, "%s [%s]\n",
timestamp ?: "(no timestamp)", timestamp ?: "(no timestamp)",
cursor); cursor);

View File

@ -414,7 +414,6 @@ int mode_to_inaccessible_node(
_cleanup_free_ char *d = NULL; _cleanup_free_ char *d = NULL;
const char *node = NULL; const char *node = NULL;
bool fallback = false;
assert(ret); assert(ret);
@ -432,12 +431,10 @@ int mode_to_inaccessible_node(
case S_IFCHR: case S_IFCHR:
node = "/systemd/inaccessible/chr"; node = "/systemd/inaccessible/chr";
fallback = true;
break; break;
case S_IFBLK: case S_IFBLK:
node = "/systemd/inaccessible/blk"; node = "/systemd/inaccessible/blk";
fallback = true;
break; break;
case S_IFIFO: case S_IFIFO:
@ -455,7 +452,24 @@ int mode_to_inaccessible_node(
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
if (fallback && access(d, F_OK) < 0) { /* On new kernels unprivileged users are permitted to create 0:0 char device nodes (because they also
* act as whiteout inode for overlayfs), but no other char or block device nodes. On old kernels no
* device node whatsoever may be created by unprivileged processes. Hence, if the caller asks for the
* inaccessible block device node let's see if the block device node actually exists, and if not,
* fall back to the character device node. From there fall back to the socket device node. This means
* in the best case we'll get the right device node type but if not we'll hopefully at least get a
* device node at all. */
if (S_ISBLK(mode) &&
access(d, F_OK) < 0 && errno == ENOENT) {
free(d);
d = path_join(runtime_dir, "/systemd/inaccessible/chr");
if (!d)
return -ENOMEM;
}
if (IN_SET(mode & S_IFMT, S_IFBLK, S_IFCHR) &&
access(d, F_OK) < 0 && errno == ENOENT) {
free(d); free(d);
d = path_join(runtime_dir, "/systemd/inaccessible/sock"); d = path_join(runtime_dir, "/systemd/inaccessible/sock");
if (!d) if (!d)

View File

@ -170,6 +170,7 @@ static bool arg_now = false;
static bool arg_jobs_before = false; static bool arg_jobs_before = false;
static bool arg_jobs_after = false; static bool arg_jobs_after = false;
static char **arg_clean_what = NULL; static char **arg_clean_what = NULL;
static TimestampStyle arg_timestamp_style = TIMESTAMP_PRETTY;
/* This is a global cache that will be constructed on first use. */ /* This is a global cache that will be constructed on first use. */
static Hashmap *cached_id_map = NULL; static Hashmap *cached_id_map = NULL;
@ -4195,7 +4196,7 @@ static void print_status_info(
i->active_exit_timestamp; i->active_exit_timestamp;
s1 = format_timestamp_relative(since1, sizeof(since1), timestamp); s1 = format_timestamp_relative(since1, sizeof(since1), timestamp);
s2 = format_timestamp(since2, sizeof(since2), timestamp); s2 = format_timestamp_style(since2, sizeof(since2), timestamp, arg_timestamp_style);
if (s1) if (s1)
printf(" since %s; %s\n", s2, s1); printf(" since %s; %s\n", s2, s1);
@ -4229,7 +4230,7 @@ static void print_status_info(
dual_timestamp_get(&nw); dual_timestamp_get(&nw);
next_elapse = calc_next_elapse(&nw, &next); next_elapse = calc_next_elapse(&nw, &next);
next_rel_time = format_timestamp_relative(tstamp1, sizeof tstamp1, next_elapse); next_rel_time = format_timestamp_relative(tstamp1, sizeof tstamp1, next_elapse);
next_time = format_timestamp(tstamp2, sizeof tstamp2, next_elapse); next_time = format_timestamp_style(tstamp2, sizeof tstamp2, next_elapse, arg_timestamp_style);
if (next_time && next_rel_time) if (next_time && next_rel_time)
printf("%s; %s\n", next_time, next_rel_time); printf("%s; %s\n", next_time, next_rel_time);
@ -4254,7 +4255,7 @@ static void print_status_info(
int n = 0; int n = 0;
s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp); s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp);
s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp); s2 = format_timestamp_style(since2, sizeof(since2), i->condition_timestamp, arg_timestamp_style);
printf(" Condition: start %scondition failed%s at %s%s%s\n", printf(" Condition: start %scondition failed%s at %s%s%s\n",
ansi_highlight_yellow(), ansi_normal(), ansi_highlight_yellow(), ansi_normal(),
@ -4276,7 +4277,7 @@ static void print_status_info(
if (!i->assert_result && i->assert_timestamp > 0) { if (!i->assert_result && i->assert_timestamp > 0) {
s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp); s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp);
s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp); s2 = format_timestamp_style(since2, sizeof(since2), i->assert_timestamp, arg_timestamp_style);
printf(" Assert: start %sassertion failed%s at %s%s%s\n", printf(" Assert: start %sassertion failed%s at %s%s%s\n",
ansi_highlight_red(), ansi_normal(), ansi_highlight_red(), ansi_normal(),
@ -5037,7 +5038,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
while ((r = sd_bus_message_read(m, "(sst)", &base, &spec, &next_elapse)) > 0) { while ((r = sd_bus_message_read(m, "(sst)", &base, &spec, &next_elapse)) > 0) {
char timestamp[FORMAT_TIMESTAMP_MAX] = "n/a"; char timestamp[FORMAT_TIMESTAMP_MAX] = "n/a";
(void) format_timestamp(timestamp, sizeof(timestamp), next_elapse); (void) format_timestamp_style(timestamp, sizeof(timestamp), next_elapse, arg_timestamp_style);
bus_print_property_valuef(name, expected_value, value, bus_print_property_valuef(name, expected_value, value,
"{ %s=%s ; next_elapse=%s }", base, spec, timestamp); "{ %s=%s ; next_elapse=%s }", base, spec, timestamp);
} }
@ -5077,8 +5078,8 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
strna(info.path), strna(info.path),
strna(tt), strna(tt),
strna(o), strna(o),
strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)), strna(format_timestamp_style(timestamp1, sizeof(timestamp1), info.start_timestamp, arg_timestamp_style)),
strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)), strna(format_timestamp_style(timestamp2, sizeof(timestamp2), info.exit_timestamp, arg_timestamp_style)),
info.pid, info.pid,
sigchld_code_to_string(info.code), sigchld_code_to_string(info.code),
info.status, info.status,
@ -5090,8 +5091,8 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
strna(info.path), strna(info.path),
strna(tt), strna(tt),
yes_no(info.ignore), yes_no(info.ignore),
strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)), strna(format_timestamp_style(timestamp1, sizeof(timestamp1), info.start_timestamp, arg_timestamp_style)),
strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)), strna(format_timestamp_style(timestamp2, sizeof(timestamp2), info.exit_timestamp, arg_timestamp_style)),
info.pid, info.pid,
sigchld_code_to_string(info.code), sigchld_code_to_string(info.code),
info.status, info.status,
@ -5754,7 +5755,7 @@ static int show_system_status(sd_bus *bus) {
printf(" Failed: %" PRIu32 " units\n", mi.n_failed_units); printf(" Failed: %" PRIu32 " units\n", mi.n_failed_units);
printf(" Since: %s; %s\n", printf(" Since: %s; %s\n",
format_timestamp(since2, sizeof(since2), mi.timestamp), format_timestamp_style(since2, sizeof(since2), mi.timestamp, arg_timestamp_style),
format_timestamp_relative(since1, sizeof(since1), mi.timestamp)); format_timestamp_relative(since1, sizeof(since1), mi.timestamp));
printf(" CGroup: %s\n", mi.control_group ?: "/"); printf(" CGroup: %s\n", mi.control_group ?: "/");
@ -7804,6 +7805,11 @@ static int systemctl_help(void) {
" --boot-loader-entry=NAME\n" " --boot-loader-entry=NAME\n"
" Boot into a specific boot loader entry on next boot\n" " Boot into a specific boot loader entry on next boot\n"
" --plain Print unit dependencies as a list instead of a tree\n" " --plain Print unit dependencies as a list instead of a tree\n"
" --timestamp=FORMAT Change format of printed timestamps.\n"
" 'pretty' (default): 'Day YYYY-MM-DD HH:MM:SS TZ\n"
" 'us': 'Day YYYY-MM-DD HH:MM:SS.UUUUUU TZ\n"
" 'utc': 'Day YYYY-MM-DD HH:MM:SS UTC\n"
" 'us+utc': 'Day YYYY-MM-DD HH:MM:SS.UUUUUU UTC\n"
"\nSee the %2$s for details.\n" "\nSee the %2$s for details.\n"
, program_invocation_short_name , program_invocation_short_name
, link , link
@ -8052,6 +8058,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_WAIT, ARG_WAIT,
ARG_WHAT, ARG_WHAT,
ARG_REBOOT_ARG, ARG_REBOOT_ARG,
ARG_TIMESTAMP_STYLE,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -8106,6 +8113,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "show-transaction", no_argument, NULL, 'T' }, { "show-transaction", no_argument, NULL, 'T' },
{ "what", required_argument, NULL, ARG_WHAT }, { "what", required_argument, NULL, ARG_WHAT },
{ "reboot-argument", required_argument, NULL, ARG_REBOOT_ARG }, { "reboot-argument", required_argument, NULL, ARG_REBOOT_ARG },
{ "timestamp", required_argument, NULL, ARG_TIMESTAMP_STYLE },
{} {}
}; };
@ -8505,6 +8513,19 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_reboot_argument = optarg; arg_reboot_argument = optarg;
break; break;
case ARG_TIMESTAMP_STYLE:
if (streq(optarg, "help")) {
DUMP_STRING_TABLE(timestamp_style, TimestampStyle, _TIMESTAMP_STYLE_MAX);
return 0;
}
arg_timestamp_style = timestamp_style_from_string(optarg);
if (arg_timestamp_style < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid value: %s.", optarg);
break;
case '.': case '.':
/* Output an error mimicking getopt, and print a hint afterwards */ /* Output an error mimicking getopt, and print a hint afterwards */
log_error("%s: invalid option -- '.'", program_invocation_name); log_error("%s: invalid option -- '.'", program_invocation_name);
@ -9130,7 +9151,7 @@ static int logind_schedule_shutdown(void) {
return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r)); return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r));
if (!arg_quiet) if (!arg_quiet)
log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", log_action, format_timestamp(date, sizeof(date), arg_when)); log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", log_action, format_timestamp_style(date, sizeof(date), arg_when, arg_timestamp_style));
return 0; return 0;
#else #else
return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),

View File

@ -58,7 +58,7 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_
u = after; u = after;
r = calendar_spec_next_usec(c, after, &u); r = calendar_spec_next_usec(c, after, &u);
printf("At: %s\n", r < 0 ? strerror_safe(r) : format_timestamp_us(buf, sizeof buf, u)); printf("At: %s\n", r < 0 ? strerror_safe(r) : format_timestamp_style(buf, sizeof buf, u, TIMESTAMP_US));
if (expect != (usec_t)-1) if (expect != (usec_t)-1)
assert_se(r >= 0 && u == expect); assert_se(r >= 0 && u == expect);
else else
@ -83,7 +83,7 @@ static void test_timestamp(void) {
x = now(CLOCK_REALTIME); x = now(CLOCK_REALTIME);
assert_se(format_timestamp_us(buf, sizeof(buf), x)); assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_US));
printf("%s\n", buf); printf("%s\n", buf);
assert_se(calendar_spec_from_string(buf, &c) >= 0); assert_se(calendar_spec_from_string(buf, &c) >= 0);
assert_se(calendar_spec_to_string(c, &t) >= 0); assert_se(calendar_spec_to_string(c, &t) >= 0);
@ -104,11 +104,11 @@ static void test_hourly_bug_4031(void) {
n = now(CLOCK_REALTIME); n = now(CLOCK_REALTIME);
assert_se((r = calendar_spec_next_usec(c, n, &u)) >= 0); assert_se((r = calendar_spec_next_usec(c, n, &u)) >= 0);
printf("Now: %s (%"PRIu64")\n", format_timestamp_us(buf, sizeof buf, n), n); printf("Now: %s (%"PRIu64")\n", format_timestamp_style(buf, sizeof buf, n, TIMESTAMP_US), n);
printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror_safe(r) : format_timestamp_us(buf, sizeof buf, u), u); printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror_safe(r) : format_timestamp_style(buf, sizeof buf, u, TIMESTAMP_US), u);
assert_se((r = calendar_spec_next_usec(c, u, &w)) >= 0); assert_se((r = calendar_spec_next_usec(c, u, &w)) >= 0);
printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror_safe(r) : format_timestamp_us(zaf, sizeof zaf, w), w); printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror_safe(r) : format_timestamp_style(zaf, sizeof zaf, w, TIMESTAMP_US), w);
assert_se(n < u); assert_se(n < u);
assert_se(u <= n + USEC_PER_HOUR); assert_se(u <= n + USEC_PER_HOUR);

View File

@ -438,20 +438,6 @@ static void test_condition_test_kernel_version(void) {
condition_free(condition); condition_free(condition);
} }
static void test_condition_test_null(void) {
Condition *condition;
condition = condition_new(CONDITION_NULL, NULL, false, false);
assert_se(condition);
assert_se(condition_test(condition, environ) > 0);
condition_free(condition);
condition = condition_new(CONDITION_NULL, NULL, false, true);
assert_se(condition);
assert_se(condition_test(condition, environ) == 0);
condition_free(condition);
}
static void test_condition_test_security(void) { static void test_condition_test_security(void) {
Condition *condition; Condition *condition;
@ -868,7 +854,6 @@ int main(int argc, char *argv[]) {
test_condition_test_architecture(); test_condition_test_architecture();
test_condition_test_kernel_command_line(); test_condition_test_kernel_command_line();
test_condition_test_kernel_version(); test_condition_test_kernel_version();
test_condition_test_null();
test_condition_test_security(); test_condition_test_security();
print_securities(); print_securities();
test_condition_test_virtualization(); test_condition_test_virtualization();

View File

@ -11,7 +11,7 @@ static void test_should_pass(const char *p) {
log_info("Test: %s", p); log_info("Test: %s", p);
assert_se(parse_timestamp(p, &t) >= 0); assert_se(parse_timestamp(p, &t) >= 0);
assert_se(format_timestamp_us(buf, sizeof(buf), t)); assert_se(format_timestamp_style(buf, sizeof(buf), t, TIMESTAMP_US));
log_info("\"%s\"\"%s\"", p, buf); log_info("\"%s\"\"%s\"", p, buf);
assert_se(parse_timestamp(buf, &q) >= 0); assert_se(parse_timestamp(buf, &q) >= 0);
@ -19,7 +19,7 @@ static void test_should_pass(const char *p) {
char tmp[FORMAT_TIMESTAMP_MAX]; char tmp[FORMAT_TIMESTAMP_MAX];
log_error("round-trip failed: \"%s\"\"%s\"", log_error("round-trip failed: \"%s\"\"%s\"",
buf, format_timestamp_us(tmp, sizeof(tmp), q)); buf, format_timestamp_style(tmp, sizeof(tmp), q, TIMESTAMP_US));
} }
assert_se(q == t); assert_se(q == t);

View File

@ -3,6 +3,7 @@
#include "capability-util.h" #include "capability-util.h"
#include "dev-setup.h" #include "dev-setup.h"
#include "fs-util.h" #include "fs-util.h"
#include "mkdir.h"
#include "path-util.h" #include "path-util.h"
#include "rm-rf.h" #include "rm-rf.h"
#include "tmpfile-util.h" #include "tmpfile-util.h"
@ -17,8 +18,8 @@ int main(int argc, char *argv[]) {
assert_se(mkdtemp_malloc("/tmp/test-dev-setupXXXXXX", &p) >= 0); assert_se(mkdtemp_malloc("/tmp/test-dev-setupXXXXXX", &p) >= 0);
f = prefix_roota(p, "/run"); f = prefix_roota(p, "/run/systemd");
assert_se(mkdir(f, 0755) >= 0); assert_se(mkdir_p(f, 0755) >= 0);
assert_se(make_inaccessible_nodes(f, 1, 1) >= 0); assert_se(make_inaccessible_nodes(f, 1, 1) >= 0);

View File

@ -333,17 +333,17 @@ static void test_format_timestamp(void) {
assert_se(parse_timestamp(buf, &y) >= 0); assert_se(parse_timestamp(buf, &y) >= 0);
assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC);
assert_se(format_timestamp_utc(buf, sizeof(buf), x)); assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_UTC));
log_info("%s", buf); log_info("%s", buf);
assert_se(parse_timestamp(buf, &y) >= 0); assert_se(parse_timestamp(buf, &y) >= 0);
assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC);
assert_se(format_timestamp_us(buf, sizeof(buf), x)); assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_US));
log_info("%s", buf); log_info("%s", buf);
assert_se(parse_timestamp(buf, &y) >= 0); assert_se(parse_timestamp(buf, &y) >= 0);
assert_se(x == y); assert_se(x == y);
assert_se(format_timestamp_us_utc(buf, sizeof(buf), x)); assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_US_UTC));
log_info("%s", buf); log_info("%s", buf);
assert_se(parse_timestamp(buf, &y) >= 0); assert_se(parse_timestamp(buf, &y) >= 0);
assert_se(x == y); assert_se(x == y);
@ -364,7 +364,7 @@ static void test_format_timestamp_utc_one(usec_t val, const char *result) {
char buf[FORMAT_TIMESTAMP_MAX]; char buf[FORMAT_TIMESTAMP_MAX];
const char *t; const char *t;
t = format_timestamp_utc(buf, sizeof(buf), val); t = format_timestamp_style(buf, sizeof(buf), val, TIMESTAMP_UTC);
assert_se(streq_ptr(t, result)); assert_se(streq_ptr(t, result));
} }

View File

@ -155,7 +155,7 @@ static int clock_state_update(
if (tx.status & STA_NANO) if (tx.status & STA_NANO)
tx.time.tv_usec /= 1000; tx.time.tv_usec /= 1000;
t = timeval_load(&tx.time); t = timeval_load(&tx.time);
ts = format_timestamp_us_utc(buf, sizeof(buf), t); ts = format_timestamp_style(buf, sizeof(buf), t, TIMESTAMP_US_UTC);
if (!ts) if (!ts)
strcpy(buf, "unrepresentable"); strcpy(buf, "unrepresentable");
log_info("adjtime state %d status %x time %s", sp->adjtime_state, tx.status, ts); log_info("adjtime state %d status %x time %s", sp->adjtime_state, tx.status, ts);

View File

@ -611,7 +611,7 @@ static int dir_cleanup(
/* Follows spelling in stat(1). */ /* Follows spelling in stat(1). */
log_debug("Directory \"%s\": modify time %s is too new.", log_debug("Directory \"%s\": modify time %s is too new.",
sub_path, sub_path,
format_timestamp_us(a, sizeof(a), age)); format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
continue; continue;
} }
@ -620,7 +620,7 @@ static int dir_cleanup(
char a[FORMAT_TIMESTAMP_MAX]; char a[FORMAT_TIMESTAMP_MAX];
log_debug("Directory \"%s\": access time %s is too new.", log_debug("Directory \"%s\": access time %s is too new.",
sub_path, sub_path,
format_timestamp_us(a, sizeof(a), age)); format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
continue; continue;
} }
@ -672,7 +672,7 @@ static int dir_cleanup(
/* Follows spelling in stat(1). */ /* Follows spelling in stat(1). */
log_debug("File \"%s\": modify time %s is too new.", log_debug("File \"%s\": modify time %s is too new.",
sub_path, sub_path,
format_timestamp_us(a, sizeof(a), age)); format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
continue; continue;
} }
@ -681,7 +681,7 @@ static int dir_cleanup(
char a[FORMAT_TIMESTAMP_MAX]; char a[FORMAT_TIMESTAMP_MAX];
log_debug("File \"%s\": access time %s is too new.", log_debug("File \"%s\": access time %s is too new.",
sub_path, sub_path,
format_timestamp_us(a, sizeof(a), age)); format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
continue; continue;
} }
@ -690,7 +690,7 @@ static int dir_cleanup(
char a[FORMAT_TIMESTAMP_MAX]; char a[FORMAT_TIMESTAMP_MAX];
log_debug("File \"%s\": change time %s is too new.", log_debug("File \"%s\": change time %s is too new.",
sub_path, sub_path,
format_timestamp_us(a, sizeof(a), age)); format_timestamp_style(a, sizeof(a), age, TIMESTAMP_US));
continue; continue;
} }
@ -713,8 +713,8 @@ finish:
log_debug("Restoring access and modification time on \"%s\": %s, %s", log_debug("Restoring access and modification time on \"%s\": %s, %s",
p, p,
format_timestamp_us(a, sizeof(a), age1), format_timestamp_style(a, sizeof(a), age1, TIMESTAMP_US),
format_timestamp_us(b, sizeof(b), age2)); format_timestamp_style(b, sizeof(b), age2, TIMESTAMP_US));
/* Restore original directory timestamps */ /* Restore original directory timestamps */
if (futimens(dirfd(d), (struct timespec[]) { if (futimens(dirfd(d), (struct timespec[]) {
@ -2228,7 +2228,7 @@ static int clean_item_instance(Item *i, const char* instance) {
log_debug("Cleanup threshold for %s \"%s\" is %s", log_debug("Cleanup threshold for %s \"%s\" is %s",
mountpoint ? "mount point" : "directory", mountpoint ? "mount point" : "directory",
instance, instance,
format_timestamp_us(timestamp, sizeof(timestamp), cutoff)); format_timestamp_style(timestamp, sizeof(timestamp), cutoff, TIMESTAMP_US));
return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint, return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
MAX_DEPTH, i->keep_first_level); MAX_DEPTH, i->keep_first_level);

View File

@ -15,9 +15,6 @@ Documentation=https://www.freedesktop.org/wiki/Software/systemd/machined
Wants=machine.slice Wants=machine.slice
After=machine.slice After=machine.slice
RequiresMountsFor=/var/lib/machines RequiresMountsFor=/var/lib/machines
ConditionNull=true
ConditionNull=
ConditionNull=|!false
OnFailureIsolate=false OnFailureIsolate=false
FailureActionExitStatus=222 FailureActionExitStatus=222
FailureActionExitStatus= FailureActionExitStatus=

View File

@ -60,7 +60,7 @@ function check_norbind {
function check_notification_socket { function check_notification_socket {
# https://github.com/systemd/systemd/issues/4944 # https://github.com/systemd/systemd/issues/4944
local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/systemd/nspawn/notify' local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/host/notify'
# /testsuite-13.nc-container is prepared by test.sh # /testsuite-13.nc-container is prepared by test.sh
systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd" systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd"
systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd" systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd"