We usually check return value of syscalls or glibc functions by it is
negative or not, something like that `if (stat(path, &st) < 0)`.
Let's also use the same style for lseek() and friends even the type of
their return value is off_t.
Note, fseeko() returns int, instead of off_t.
Currently, when we deserialize an fd we do a lot of manual work. Add a
common helper that makes this more robust and uniform.
Note that this sometimes changes behaviour slightly, but in ways that
shouldn't really matter: if we fail to deserialize an fd correctly we'll
unset (i.e. set to -EBADF) the fd in the deserialized data structure.
Previously, we'd leave the old value in place.
This should not change effective result (as in either case we'll be in a
bad state afterwards, just once we mix old/invalidated state with new
state, while now we'll reset the state explicitly to invalidated state
on failure). In particular as deserialization starts from an empty
structure generally, hence the old value should be unset anyway.
Another slight change is that if we fail to deserialize some object half
way, and we already have taken out one fd from the serialized fdset
we'll now just close it instead of returning it to/leaving it in the
fdset. Given that such "orphaned" fds are blanket closed after
deserialization finishes this also shouldn't change behaviour IRL.
Also, the idle_pipe was previously incorrectly serialized: we'd
serialize invalidated fds, which would fail, but because parsing errors
on this were ignored on the deserializatin noone noticed. This is fixed.
No functional changes, only moving code that is only needed in
exec_invoke, and adding new dependencies for seccomp/selinux/apparmor/pam
in meson for the sd-executor binary.
Currently we spawn services by forking a child process, doing a bunch
of work, and then exec'ing the service executable.
There are some advantages to this approach:
- quick: we immediately have access to all the enourmous amount of
state simply by virtue of sharing the memory with the parent
- easy to refactor and add features
- part of the same binary, will never be out of sync
There are however significant drawbacks:
- doing work after fork and before exec is against glibc's supported
case for several APIs we call
- copy-on-write trap: anytime any memory is touched in either parent
or child, a copy of that page will be triggered
- memory footprint of the child process will be memory footprint of
PID1, but using the cgroup memory limits of the unit
The last issue is especially problematic on resource constrained
systems where hard memory caps are enforced and swap is not allowed.
As soon as PID1 is under load, with no page out due to no swap, and a
service with a low MemoryMax= tries to start, hilarity ensues.
Add a new systemd-executor binary, that is able to receive all the
required state via memfd, deserialize it, prepare the appropriate
data structures and call exec_child.
Use posix_spawn which uses CLONE_VM + CLONE_VFORK, to ensure there is
no copy-on-write (same address space will be used, and parent process
will be frozen, until exec).
The sd-executor binary is pinned by FD on startup, so that we can
guarantee there will be no incompatibilities during upgrades.
Before this commit, $USER, $HOME, $LOGNAME and $SHELL are only
set when User= is set for the unit. For system service, this
results in different behaviors depending on whether User=root is set.
$USER always makes sense on its own, so let's set it unconditionally.
Ideally $HOME should be set too, but it causes trouble when e.g. getty
passes '-p' to login(1), which then doesn't override $HOME. $LOGNAME and
$SHELL are more like "login environments", and are generally not
suitable for system services. Therefore, a new option SetLoginEnvironment=
is also added to control the latter three variables.
Fixes#23438
Replaces #8227
When we're starting early boot services such as systemd-userdbd.service,
/tmp might not yet be mounted, so let's use a directory in /run instead
which is guaranteed to be available.
This adds a new structure UnitDefaults which embedds the various default
settings for units we maintain. We so far maintained two sets of
variables for this, one in main.c as static variables and one in the
Manager structure. This moves them into a common structure.
This is most just search/replace, i.e. very dumb refactoring.
The fact that we now use a common structure for this allows us further
refactorings later.
Inspired by the discussions on #27890
This reverts commits
- 9ae3624889
"test-execute: add tests for credentials directory with mount namespace"↲
- 94fe4cf255
"core: do not leak mount for credentials directory if mount namespace is enabled",
- 7241b9cd72
"core/credential: make setup_credentials() return path to credentials directory",
- fbaf3b23ae
"core: set $CREDENTIALS_DIRECTORY only when we set up credentials"
Before the commits, credentials directory set up on ExecStart= was kept
on e.g. ExecStop=. But, with the changes, if a service requests a
private mount namespace, the credentials directory is discarded after
ExecStart= is finished.
Let's revert the change, and find better way later.
Addresses the post-merge comment
https://github.com/systemd/systemd/pull/28787#issuecomment-1690614202.
Since kernel v5.2, open_tree() and move_mount() are added. If a service
loads or sets credentials, then let's try to clone the mount that contains
credentials with open_tree(), then mount it after a (private) mount
namespace is initialized for the service. Then, we can setup a mount for
credentials directory without leaking it to the main shared mount
namespace.
With this change, the credentials for services that request their own
private mount namespace become much much safer. And, the number of mount
events triggered by setting up credential directories can be decreased.
Unfortunately, this does not 'fix' the original issue #25527, as the
reported service does not requests private mount namespace, but the
situation should be better now.
seccomp-util.h doesn't need ifdeffing, hence don't. It has worked since
quite a while with HAVE_SECCOMP is off, hence use it everywhere.
Also drop explicit seccomp.h inclusion everywhere (which needs
HAVE_SECCOMP ifdeffery everywhere). seccomp-util.h includes it anyway,
automatically, which we can just rely on, and it deals with HAVE_SECCOMP
at one central place.
If someone reads /run/host/os-release at the exact same time it is being updated, and it
is large enough, they might read a half-written file. This is very unlikely as
os-release is typically small and very rarely changes, but it is not
impossible.
Bind mount a staging directory instead of the file, and symlink the file
into into, so that we can do atomic file updates and close this gap.
Atomic replacement creates a new inode, so existing bind mounts would
continue to see the old file, and only new services would see the new file.
The indirection via the directory allows to work around this, as the
directory is fixed and never changes so the bind mount is always valid,
and its content is shared with all existing services.
Fixes https://github.com/systemd/systemd/issues/28794
Follow-up for 3f37a82545
exec_child() is supposed to set *exit_status when returning failure.
Unfortunately, we didn't do that in two cases. The result would be:
- a bogus error message "Failed at step SUCCESS spawning foo: …",
- a bogus success exit status.
Bugs introduced in 390902012c and
ad21e542b2.
The code is reworked to add some asserts and not set exit_status in the caller
so that it's clearer (also to the compiler) that it needs to be set.
For a userns root user to be able to access the credentials, both
the uid and gid of the credentials directory have to be mapped into
the userns. Currently, the credentials directory group is root, which
we obviously do not want to map in to a userns, so let's make sure
that the credentials directory and files are owned by the service
group instead, which can generally be safely mapped into the userns.
Since we use permissions mode 0600, this shouldn't cause any change
in who is able to access the credentials.
Fixes#28747
Given that ERRNO_IS_PRIVILEGE() also matches positive values,
make sure this macro is not called with arguments that do not have
errno semantics.
In this case the arguments passed to ERRNO_IS_PRIVILEGE() are the values
returned by set_oom_score_adjust() and set_coredump_filter() which are
not expected to return any positive values, but let's be consistent
anyway and move the ERRNO_IS_PRIVILEGE() invocations to the branches
where the return values are known to be negative.
Currently for portable services we automatically add a bind mount
os-release -> /run/host/os-release. This becomes problematic for the
soft-reboot case, as it's likely that portable services will be configured
to survive it, and thus would forever keep a reference to the old host's
os-release, which would be a problem because it becomes outdated, and also
it stops the old rootfs from being garbage collected.
Create a copy when the manager starts under /run/systemd/propagate instead,
and bind mount that for all services using RootDirectory=/RootImage=, so
that on soft-reboot the content gets updated (without creating a new file,
so the existing bind mounts will see the new content too).
This expands the /run/host/os-release protocol to more services, but I
think that's a nice thing to have too.
Closes https://github.com/systemd/systemd/issues/28023
When systemd-firstboot (or any other unit which uses the tty) is started,
systemd will reset the terminal. If systemd-vconsole-setup happens to be
running at that time, it'll error out when it tries to use the vconsole fd and
gets an EIO from ioctl.
e019ea738d was the first fix. It added an
implicit ordering between units using the tty and systemd-vconsole-setup.
(The commit title is wrong. The approach was generalized, but the commit title
wasn't updated.)
Then cea32691c3 was added to restart
systemd-vconsole-setup.service from systemd-firstboot.service. This was OK,
with the ordering in place, systemd-vconsole-setup.service would wait until
systemd-firstboot.service exited. But this wasn't enough, because we want the
key mappings to be loaded immediately after systemd-firstboot writes the
config. 8eb668b9ab implemented that, but actually
reintroduced the original issue. I had to drop the ordering between the two
units because otherwise we'd deadlock, waiting from firstboot for
vconsole-setup which wouldn't start while firstboot was running.
Restarting vconsole-setup.service from systemd-firstboot.service works just
fine, but when vconsole-setup.service is started earlier, it may be interrupted
by systemd-firstboot.service.
To resolve the issue, let's take a lock around the tty device. The reset is
performed after fork, so the (short) delay should not matter too much.
In xopenat_lock() the assert on <path> is dropped so that we can call
xopenat(fd, NULL) to get a copy of the original fd.
Fixes#26908.
This reverts commit e019ea738d.
In the new approach, a lock on /dev/console will be used. This lock will solve
the issue for services which run in early boot. Services which run later are
ordered after sysinit.target, so they'll run much later anyway so this
automatic dependency is not useful. Let's remove it again to make the code
simpler.