Compare commits

..

No commits in common. "990307c3da61b16c57d958910295b96ea6aa2a5e" and "a4df0c004a0e42dfbad02e9595420fcef3965b85" have entirely different histories.

43 changed files with 402 additions and 516 deletions

3
TODO
View File

@ -19,6 +19,9 @@ 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,17 +25,6 @@ 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,13 +172,6 @@ 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`
@ -196,62 +189,6 @@ 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,20 +2134,6 @@ 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,42 +261,53 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>MemoryMin=<replaceable>bytes</replaceable></varname>, <varname>MemoryLow=<replaceable>bytes</replaceable></varname></term> <term><varname>MemoryMin=<replaceable>bytes</replaceable></varname></term>
<listitem> <listitem>
<para>Specify the memory usage protection of the executed processes in this unit. <para>Specify the memory usage protection of the executed processes in this unit. If the memory usages of
When reclaiming memory, the unit is treated as if it was using less memory resulting in memory this unit and all its ancestors are below their minimum boundaries, this unit's memory won't be reclaimed.</para>
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> or <literal>memory.low</literal> control group attribute. This controls the <literal>memory.min</literal> control group attribute. For details about this
For details about this control group attribute, see <ulink 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> or <para>Units may have their children use a default <literal>memory.min</literal> value by specifying
<literal>memory.low</literal> value by specifying <varname>DefaultMemoryMin=</varname> or <varname>DefaultMemoryMin=</varname>, which has the same semantics as <varname>MemoryMin=</varname>. This setting
<varname>DefaultMemoryLow=</varname>, which has the same semantics as does not affect <literal>memory.min</literal> in the unit itself.</para>
<varname>MemoryMin=</varname> and <varname>MemoryLow=</varname>. </listitem>
This setting does not affect <literal>memory.min</literal> or <literal>memory.low</literal> </varlistentry>
in the unit itself.
Using it to set a default child allocation is only useful on kernels older than 5.7, <varlistentry>
which do not support the <literal>memory_recursiveprot</literal> cgroup2 mount option.</para> <term><varname>MemoryLow=<replaceable>bytes</replaceable></varname></term>
<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,6 +1092,9 @@
<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 --timestamp' --preset-mode -n --lines -o --output -M --machine --message'
) )
if __contains_word "--user" ${COMP_WORDS[*]}; then if __contains_word "--user" ${COMP_WORDS[*]}; then
@ -176,9 +176,6 @@ _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,13 +431,6 @@ 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).
@ -478,5 +471,4 @@ _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,28 +8,84 @@
#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) {
assert(u); const char *p;
assert(line);
for (ConditionType t = 0; t < _CONDITION_TYPE_MAX; t++) {
ConfigParserCallback callback;
Condition **target; Condition **target;
const char *p, *name;
name = condition_type_to_string(t); if ((p = startswith(line, "Condition")))
p = startswith(line, name);
if (p)
target = &u->conditions; target = &u->conditions;
else { else if ((p = startswith(line, "Assert")))
name = assert_type_to_string(t); target = &u->asserts;
p = startswith(line, name); else
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 != '=')
@ -38,12 +94,7 @@ static int parse_condition(Unit *u, const char *line) {
p += strspn(p, WHITESPACE); p += strspn(p, WHITESPACE);
if (condition_takes_path(t)) return c->parser(NULL, "(stdin)", 0, NULL, 0, c->name, c->type, p, target, u);
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,47 +8,38 @@
#include <linux/stat.h> #include <linux/stat.h>
#endif #endif
/* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */ /* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
#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 {
struct statx 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 __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
@ -58,13 +49,3 @@ struct new_statx STATX_DEFINITION;
#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(dfd, filename, flags, mask, buffer) missing_statx(dfd, filename, flags, mask, buffer) # define statx missing_statx
#endif #endif
#if !HAVE_SET_MEMPOLICY #if !HAVE_SET_MEMPOLICY

View File

@ -8,8 +8,6 @@
#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"
@ -34,8 +32,6 @@ 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.
@ -90,16 +86,13 @@ int name_to_handle_at_loop(
} }
} }
static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mnt_id) { static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *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 {
@ -128,7 +121,7 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mn
p += strspn(p, WHITESPACE); p += strspn(p, WHITESPACE);
p[strcspn(p, WHITESPACE)] = 0; p[strcspn(p, WHITESPACE)] = 0;
return safe_atoi(p, ret_mnt_id); return safe_atoi(p, 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) {
@ -136,46 +129,33 @@ 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 statx()' STATX_ATTR_MOUNT_ROOT attribute, which is our ideal API, available /* First we will try the name_to_handle_at() syscall, which
* since kernel 5.8. * tells us the mount id and an opaque file "handle". It is
* 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 fails, our second try is the name_to_handle_at() syscall, which tells us the mount id and * If that didn't work we will try to read the mount id from
* an opaque file "handle". It is not supported everywhere though (kernel compile-time option, not * /proc/self/fdinfo/<fd>. This is almost as good as
* all file systems are hooked up). If it works the mount id is usually good enough to tell us * name_to_handle_at(), however, does not return the
* whether something is a mount point. * opaque file handle. The opaque file handle is pretty useful
* * to detect the root directory, which we should always
* If that didn't work we will try to read the mount id from /proc/self/fdinfo/<fd>. This is almost * consider a mount point. Hence we use this only as
* as good as name_to_handle_at(), however, does not return the opaque file handle. The opaque file * fallback. Exporting the mnt_id in fdinfo is a pretty recent
* 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 comparisons. This is how things were * As last fallback we do traditional fstat() based st_dev
* traditionally done, but unionfs breaks this since it exposes file systems with a variety of st_dev * comparisons. This is how things were traditionally done,
* reported. Also, btrfs subvolumes have different st_dev, even though they aren't real mounts of * but unionfs breaks this since it exposes file
* their own. */ * systems with a variety of st_dev reported. Also, btrfs
* subvolumes have different st_dev, even though they aren't
if (statx(fd, filename, (FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW) | * real mounts of their own. */
(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))
@ -298,29 +278,8 @@ 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,7 +23,6 @@
#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"
@ -283,11 +282,12 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) {
return tv; return tv;
} }
char *format_timestamp_style( static char *format_timestamp_internal(
char *buf, char *buf,
size_t l, size_t l,
usec_t t, usec_t t,
TimestampStyle style) { bool utc,
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,27 +304,9 @@ char *format_timestamp_style(
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 */
@ -398,6 +380,22 @@ char *format_timestamp_style(
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;
@ -1570,27 +1568,3 @@ 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,15 +29,6 @@ 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)
@ -116,14 +107,13 @@ 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_style(char *buf, size_t l, usec_t t, TimestampStyle style); char *format_timestamp(char *buf, size_t l, usec_t t);
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);
@ -195,6 +185,3 @@ 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,16 +491,6 @@ 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,11 +1974,14 @@ 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;
@ -1989,9 +1992,14 @@ 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,52 +272,46 @@ 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.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, 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.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.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.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, 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.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, 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.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.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.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, 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,6 +2999,60 @@ 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,
@ -5212,6 +5266,7 @@ 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,6 +58,7 @@ 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,7 +32,6 @@
#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"
@ -54,7 +53,6 @@
#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"
@ -2075,20 +2073,6 @@ 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,8 +85,6 @@ 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,
@ -538,17 +536,8 @@ 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. If we run in a container the host might have created these for us already * inaccessible nodes from. */
* in /run/host/inaccessible/. Use those if we can, since tht way we likely get access to block/char (void) make_inaccessible_nodes(NULL, UID_INVALID, GID_INVALID);
* 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_style(buf, l, t, TIMESTAMP_UTC); return format_timestamp_utc(buf, l, t);
return format_timestamp(buf, l, t); return format_timestamp(buf, l, t);
} }

View File

@ -544,11 +544,9 @@ 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);
if (k < 0) else {
k = -errno;
} else {
mh = (struct msghdr) { mh = (struct msghdr) {
.msg_iov = &iov, .msg_iov = &iov,
.msg_iovlen = 1, .msg_iovlen = 1,
@ -1189,11 +1187,9 @@ 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);
if (k < 0) else {
k = -errno;
} else {
mh = (struct msghdr) { mh = (struct msghdr) {
.msg_iov = &iov, .msg_iov = &iov,
.msg_iovlen = 1, .msg_iovlen = 1,

View File

@ -7,10 +7,9 @@
#include "bus-error.h" #include "bus-error.h"
#include "dev-setup.h" #include "dev-setup.h"
#include "format-util.h"
#include "fs-util.h" #include "fs-util.h"
#include "format-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"
@ -33,16 +32,12 @@ 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)
log_warning_errno(r, "Failed to acquire runtime directory size, ignoring: %s", bus_error_message(&error, r)); return log_error_errno(r, "Failed to acquire runtime directory size: %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)
log_warning_errno(r, "Failed to acquire number of inodes for runtime directory, ignoring: %s", bus_error_message(&error, r)); return log_error_errno(r, "Failed to acquire number of inodes for runtime directory: %s", bus_error_message(&error, r));
*inodes = DIV_ROUND_UP(*size, 4096);
}
return 0; return 0;
} }
@ -88,8 +83,7 @@ static int user_mkdir_runtime_path(
goto fail; goto fail;
} }
log_debug_errno(errno, log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s.\n"
"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);
@ -104,6 +98,8 @@ 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,8 +978,9 @@ 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, *q; const char *mount_inside;
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]);
@ -1000,11 +1001,12 @@ 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);
(void) mknod(dest, S_IFREG|0600, 0); safe_close(open(dest, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600));
} }
} }
mount_inside = strjoina("/run/host/incoming/", basename(mount_outside)); /* Fifth, move the mount to the right place inside */
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,8 +101,10 @@
#include "user-util.h" #include "user-util.h"
#include "util.h" #include "util.h"
/* The notify socket inside the container it can use to talk to nspawn using the sd_notify(3) protocol */ /* nspawn is listening on the socket at the path in the constant nspawn_notify_socket_path
#define NSPAWN_NOTIFY_SOCKET_PATH "/run/host/notify" * nspawn_notify_socket_path is relative to the container
* 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
@ -2515,15 +2517,19 @@ 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/host", 0755, 0, 0); r = userns_mkdir(root, "/run/systemd", 0755, 0, 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create /run/host: %m"); return log_error_errno(r, "Failed to create /run/systemd: %m");
r = userns_mkdir(root, "/run/host/incoming", 0600, 0, 0); r = userns_mkdir(root, "/run/systemd/nspawn", 0755, 0, 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create /run/host/incoming: %m"); return log_error_errno(r, "Failed to create /run/systemd/nspawn: %m");
q = prefix_roota(root, "/run/host/incoming"); r = userns_mkdir(root, "/run/systemd/nspawn/incoming", 0600, 0, 0);
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;
@ -2532,7 +2538,8 @@ 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 supported for non-shared mounts. */ /* machined will MS_MOVE into that directory, and that's only
* 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);
} }
@ -3271,7 +3278,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_notify_child(void) { static int setup_sd_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,
@ -3523,7 +3530,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/host"); p = prefix_roota(directory, "/run");
(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);
@ -3564,14 +3571,6 @@ 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,
@ -3589,7 +3588,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_notify_child(); fd = setup_sd_notify_child();
if (fd < 0) if (fd < 0)
return fd; return fd;
@ -3802,7 +3801,7 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r
return 0; return 0;
} }
static int setup_notify_parent(sd_event *event, int fd, pid_t *inner_child_pid, sd_event_source **notify_event_source) { static int setup_sd_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);
@ -4633,7 +4632,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_notify_parent(event, notify_socket, PID_TO_PTR(*pid), &notify_event_source); r = setup_sd_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); assert((!parameter) == (type == CONDITION_NULL));
c = new(Condition, 1); c = new(Condition, 1);
if (!c) if (!c)
@ -776,6 +776,15 @@ 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) = {
@ -802,6 +811,7 @@ 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,
@ -849,20 +859,23 @@ 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 ? "!" : "",
c->parameter); p);
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 ? "!" : "",
c->parameter, p,
condition_result_to_string(c->result)); condition_result_to_string(c->result));
} }
@ -924,6 +937,7 @@ 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",
@ -955,6 +969,7 @@ 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,6 +34,8 @@ 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 *parent_dir, const char *runtime_dir,
uid_t uid, uid_t uid,
gid_t gid) { gid_t gid) {
@ -65,26 +65,28 @@ int make_inaccessible_nodes(
const char *name; const char *name;
mode_t mode; mode_t mode;
} table[] = { } table[] = {
{ "inaccessible", S_IFDIR | 0755 }, { "/systemd", S_IFDIR | 0755 },
{ "inaccessible/reg", S_IFREG | 0000 }, { "/systemd/inaccessible", S_IFDIR | 0000 },
{ "inaccessible/dir", S_IFDIR | 0000 }, { "/systemd/inaccessible/reg", S_IFREG | 0000 },
{ "inaccessible/fifo", S_IFIFO | 0000 }, { "/systemd/inaccessible/dir", S_IFDIR | 0000 },
{ "inaccessible/sock", S_IFSOCK | 0000 }, { "/systemd/inaccessible/fifo", S_IFIFO | 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. */
{ "inaccessible/chr", S_IFCHR | 0000 }, { "/systemd/inaccessible/chr", S_IFCHR | 0000 },
{ "inaccessible/blk", S_IFBLK | 0000 }, { "/systemd/inaccessible/blk", S_IFBLK | 0000 },
}; };
_cleanup_umask_ mode_t u; _cleanup_umask_ mode_t u;
size_t i;
int r; int r;
if (!parent_dir) if (!runtime_dir)
parent_dir = "/run/systemd"; runtime_dir = "/run";
u = umask(0000); u = umask(0000);
@ -93,10 +95,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 (size_t i = 0; i < ELEMENTSOF(table); i++) { for (i = 0; i < ELEMENTSOF(table); i++) {
_cleanup_free_ char *path = NULL; _cleanup_free_ char *path = NULL;
path = path_join(parent_dir, table[i].name); path = path_join(runtime_dir, table[i].name);
if (!path) if (!path)
return log_oom(); return log_oom();
@ -105,6 +107,7 @@ 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 *parent_dir, uid_t uid, gid_t gid); int make_inaccessible_nodes(const char *root, 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_style(p, FORMAT_TIMESTAMP_MAX, d->timestamp, TIMESTAMP_UTC); ret = format_timestamp_utc(p, FORMAT_TIMESTAMP_MAX, d->timestamp);
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_style(buf, sizeof(buf), x, TIMESTAMP_UTC); k = format_timestamp_utc(buf, sizeof(buf), x);
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 = format_timestamp_style(ts, sizeof ts, realtime, timestamp = flags & OUTPUT_UTC ? format_timestamp_us_utc(ts, sizeof ts, realtime)
flags & OUTPUT_UTC ? TIMESTAMP_US_UTC : TIMESTAMP_US); : format_timestamp_us(ts, sizeof ts, realtime);
fprintf(f, "%s [%s]\n", fprintf(f, "%s [%s]\n",
timestamp ?: "(no timestamp)", timestamp ?: "(no timestamp)",
cursor); cursor);

View File

@ -414,6 +414,7 @@ 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);
@ -431,10 +432,12 @@ 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:
@ -452,24 +455,7 @@ int mode_to_inaccessible_node(
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
/* On new kernels unprivileged users are permitted to create 0:0 char device nodes (because they also if (fallback && access(d, F_OK) < 0) {
* 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,7 +170,6 @@ 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;
@ -4196,7 +4195,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_style(since2, sizeof(since2), timestamp, arg_timestamp_style); s2 = format_timestamp(since2, sizeof(since2), timestamp);
if (s1) if (s1)
printf(" since %s; %s\n", s2, s1); printf(" since %s; %s\n", s2, s1);
@ -4230,7 +4229,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_style(tstamp2, sizeof tstamp2, next_elapse, arg_timestamp_style); next_time = format_timestamp(tstamp2, sizeof tstamp2, next_elapse);
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);
@ -4255,7 +4254,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_style(since2, sizeof(since2), i->condition_timestamp, arg_timestamp_style); s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
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(),
@ -4277,7 +4276,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_style(since2, sizeof(since2), i->assert_timestamp, arg_timestamp_style); s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp);
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(),
@ -5038,7 +5037,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_style(timestamp, sizeof(timestamp), next_elapse, arg_timestamp_style); (void) format_timestamp(timestamp, sizeof(timestamp), next_elapse);
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);
} }
@ -5078,8 +5077,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_style(timestamp1, sizeof(timestamp1), info.start_timestamp, arg_timestamp_style)), strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
strna(format_timestamp_style(timestamp2, sizeof(timestamp2), info.exit_timestamp, arg_timestamp_style)), strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
info.pid, info.pid,
sigchld_code_to_string(info.code), sigchld_code_to_string(info.code),
info.status, info.status,
@ -5091,8 +5090,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_style(timestamp1, sizeof(timestamp1), info.start_timestamp, arg_timestamp_style)), strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
strna(format_timestamp_style(timestamp2, sizeof(timestamp2), info.exit_timestamp, arg_timestamp_style)), strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
info.pid, info.pid,
sigchld_code_to_string(info.code), sigchld_code_to_string(info.code),
info.status, info.status,
@ -5755,7 +5754,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_style(since2, sizeof(since2), mi.timestamp, arg_timestamp_style), format_timestamp(since2, sizeof(since2), mi.timestamp),
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 ?: "/");
@ -7805,11 +7804,6 @@ 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
@ -8058,7 +8052,6 @@ 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[] = {
@ -8113,7 +8106,6 @@ 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 },
{} {}
}; };
@ -8513,19 +8505,6 @@ 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);
@ -9151,7 +9130,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_style(date, sizeof(date), arg_when, arg_timestamp_style)); log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", log_action, format_timestamp(date, sizeof(date), arg_when));
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_style(buf, sizeof buf, u, TIMESTAMP_US)); printf("At: %s\n", r < 0 ? strerror_safe(r) : format_timestamp_us(buf, sizeof buf, u));
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_style(buf, sizeof(buf), x, TIMESTAMP_US)); assert_se(format_timestamp_us(buf, sizeof(buf), x));
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_style(buf, sizeof buf, n, TIMESTAMP_US), n); printf("Now: %s (%"PRIu64")\n", format_timestamp_us(buf, sizeof buf, n), n);
printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror_safe(r) : format_timestamp_style(buf, sizeof buf, u, TIMESTAMP_US), u); printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror_safe(r) : format_timestamp_us(buf, sizeof buf, u), 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_style(zaf, sizeof zaf, w, TIMESTAMP_US), w); printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror_safe(r) : format_timestamp_us(zaf, sizeof zaf, w), 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,6 +438,20 @@ 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;
@ -854,6 +868,7 @@ 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_style(buf, sizeof(buf), t, TIMESTAMP_US)); assert_se(format_timestamp_us(buf, sizeof(buf), t));
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_style(tmp, sizeof(tmp), q, TIMESTAMP_US)); buf, format_timestamp_us(tmp, sizeof(tmp), q));
} }
assert_se(q == t); assert_se(q == t);

View File

@ -3,7 +3,6 @@
#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"
@ -18,8 +17,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/systemd"); f = prefix_roota(p, "/run");
assert_se(mkdir_p(f, 0755) >= 0); assert_se(mkdir(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_style(buf, sizeof(buf), x, TIMESTAMP_UTC)); assert_se(format_timestamp_utc(buf, sizeof(buf), x));
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_style(buf, sizeof(buf), x, TIMESTAMP_US)); assert_se(format_timestamp_us(buf, sizeof(buf), x));
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_style(buf, sizeof(buf), x, TIMESTAMP_US_UTC)); assert_se(format_timestamp_us_utc(buf, sizeof(buf), x));
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_style(buf, sizeof(buf), val, TIMESTAMP_UTC); t = format_timestamp_utc(buf, sizeof(buf), val);
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_style(buf, sizeof(buf), t, TIMESTAMP_US_UTC); ts = format_timestamp_us_utc(buf, sizeof(buf), t);
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_style(a, sizeof(a), age, TIMESTAMP_US)); format_timestamp_us(a, sizeof(a), age));
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_style(a, sizeof(a), age, TIMESTAMP_US)); format_timestamp_us(a, sizeof(a), age));
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_style(a, sizeof(a), age, TIMESTAMP_US)); format_timestamp_us(a, sizeof(a), age));
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_style(a, sizeof(a), age, TIMESTAMP_US)); format_timestamp_us(a, sizeof(a), age));
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_style(a, sizeof(a), age, TIMESTAMP_US)); format_timestamp_us(a, sizeof(a), age));
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_style(a, sizeof(a), age1, TIMESTAMP_US), format_timestamp_us(a, sizeof(a), age1),
format_timestamp_style(b, sizeof(b), age2, TIMESTAMP_US)); format_timestamp_us(b, sizeof(b), age2));
/* 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_style(timestamp, sizeof(timestamp), cutoff, TIMESTAMP_US)); format_timestamp_us(timestamp, sizeof(timestamp), cutoff));
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,6 +15,9 @@ 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/host/notify' local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/systemd/nspawn/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"