Compare commits

...

47 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek 89f3ba7995
Merge pull request #14557 from poettering/root-storage-daemons-markdown
docs: convert root storage daemon doc to markdown
2020-01-13 20:57:37 +01:00
Lennart Poettering 180f7c26aa docs: import initrd interface documentation from fdo wiki
Imported from https://www.freedesktop.org/wiki/Software/systemd/InitrdInterface/
2020-01-13 20:55:57 +01:00
Lennart Poettering f8349d2fa5 docs: various small fixes to PORTABILITY_AND_STABILITY markdown
Nothing particularly world moving.

Follow-up for #14549
2020-01-13 20:12:12 +01:00
Lennart Poettering 0bdd282a4e killall: update reference to root storage daemon interface docs 2020-01-13 18:53:46 +01:00
Lennart Poettering 6e47cac0aa docs: convert root storage daemon doc to markdown
Import from:

https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
2020-01-13 18:52:56 +01:00
Lennart Poettering 765d88698f
Merge pull request #14400 from keszybz/alias-check
Alias check rework
2020-01-13 18:03:13 +01:00
Lennart Poettering 04d8507f68
Merge pull request #14381 from keszybz/ifindex-cleanup
Resolve alternative names
2020-01-13 17:57:59 +01:00
Lennart Poettering 200427cf7c
Merge pull request #11199 from dargad/restore-pam-setcred
Restore call to pam_setcred
2020-01-13 17:44:34 +01:00
Lennart Poettering 0b9da3d9e8
Merge pull request #14293 from keur/systemctl_with_dependencies
systemctl: Add --with-dependencies switch
2020-01-13 17:42:55 +01:00
Zeyu DONG 61c0ac0924 hwdb: Entry for Lenovo Ideapad 310S-14ISK Alps Touchpad
Add the resolution and range information for Lenovo Ideapad 310S-14ISK Alps touchpad. It provides information for the test case of libinput for Alps devices.
2020-01-13 17:42:30 +01:00
Timo Schlüßler 23b3921663 journalctl: Correctly handle combination of --reverse and --lines (fixes #1596) 2020-01-13 17:35:06 +01:00
Timo Schlüßler 3ac9cac7f7 journalctl: Correctly handle --show-cursor in combination with --until or --since and --reverse 2020-01-13 17:33:50 +01:00
Lennart Poettering 575a915a74
Merge pull request #14532 from poettering/namespace-dynamic-user-fix
Make DynamicUser=1 work in a userns container
2020-01-13 16:47:15 +01:00
Zbigniew Jędrzejewski-Szmek 5749698031
Merge pull request #14553 from poettering/man-mkdir
man: small man page fixes
2020-01-13 15:35:54 +01:00
Lennart Poettering f9951b0cf0 man: we support bind mounting regular files too
Let's be precise here: we accept non-directories here too, even though
we create things as directories if we find the Where= not to exist.
2020-01-13 11:24:35 +01:00
Lennart Poettering 151a7133cd man: document that we mkdir() on What= in .mount units too
As suggested here:

https://lists.freedesktop.org/archives/systemd-devel/2020-January/043914.html
2020-01-13 11:23:59 +01:00
Dariusz Gadomski c6cecb744b test: Add tests for gid list ops 2020-01-13 10:29:20 +01:00
Dariusz Gadomski afb11bf1b8 execute: Detect groups added by PAM and merge them with supplementary groups 2020-01-13 10:29:20 +01:00
Dariusz Gadomski 3bb39ea936 execute: Restore call to pam_setcred 2020-01-13 10:29:20 +01:00
Dariusz Gadomski 0c5d667932 user-util: Add helper functions for gid lists operations 2020-01-13 10:29:10 +01:00
Zbigniew Jędrzejewski-Szmek 117caf3765 networkctl: break long line 2020-01-12 11:24:35 +01:00
Zbigniew Jędrzejewski-Szmek 8571210a21 machinectl: reduce scope of iterator variables 2020-01-12 11:24:35 +01:00
Zbigniew Jędrzejewski-Szmek 957d9df388 resolvectl: minor optimizations to allocate less 2020-01-12 11:24:35 +01:00
Zbigniew Jędrzejewski-Szmek d308bb99d2 Resolve alternative ifnames wherever we would resolve an interface name
To keep the names manageable, "ifname_or_ifindex" is replaced by "interface".
2020-01-12 11:24:35 +01:00
Zbigniew Jędrzejewski-Szmek fc2ea97ad0 util-lib: add function to resolve "alternative" names
Calls to if_nametoindex() are expected to use resolve_ifname() instead.
2020-01-12 10:54:31 +01:00
Zbigniew Jędrzejewski-Szmek 5c3fa98db6 util-lib: move things that parse ifnames to shared/
In subsequent commits, calls to if_nametoindex() will be replaced by a wrapper
that falls back to alternative name resolution over netlink. netlink support
requires libsystemd (for sd-netlink), and we don't want to add any functions
that require netlink in basic/. So stuff that calls if_nametoindex() for user
supplied interface names, and everything that depends on that, needs to be
moved.
2020-01-11 12:07:28 +01:00
Zbigniew Jędrzejewski-Szmek 955bb7fac3 basic/socket-util: indent for clarity 2020-01-11 12:07:28 +01:00
Zbigniew Jędrzejewski-Szmek bad7cecc0a sd-netlink: do not require rtnl pointer to be passed 2020-01-11 12:07:28 +01:00
Zbigniew Jędrzejewski-Szmek 231d9de1e3 networkctl: define a helper for interface name resolution 2020-01-11 12:07:28 +01:00
Zbigniew Jędrzejewski-Szmek 9030b50a7b timedatectl: drop ifindex output parameter too 2020-01-11 12:07:28 +01:00
Zbigniew Jędrzejewski-Szmek 597da51bae tree-wide: make parse_ifindex simply return the index
We don't need a seperate output parameter that is of type int.  glibc() says
that the type is "unsigned", but the kernel thinks it's "int".  And the
"alternative names" interface also uses ints. So let's standarize on ints,
since it's clearly not realisitic to have interface numbers in the upper half
of unsigned int range.
2020-01-11 12:06:08 +01:00
Zbigniew Jędrzejewski-Szmek 2e93770fd8 man: document alias rules and aliases dropin loading 2020-01-10 14:31:28 +01:00
Zbigniew Jędrzejewski-Szmek 1bf1558552 core,install: allow one more case of "instance propagation"
If we have a template unit template@.service, it should be allowed to specify a
dependency on a unit without an instance, bar@.service. When the unit is created,
the instance will be propagated into the target, so template@inst.service will
depend on bar@inst.service.

This commit changes unit_dependency_name_compatible(), which makes the manager
accept links like that, and unit_file_verify_alias(), so that the installation
function will agree to create a symlink like that, and finally the tests are
adjusted to pass.
2020-01-10 14:31:01 +01:00
Zbigniew Jędrzejewski-Szmek 3f57bc2267 shared/install: rework alias check and add test
This mostly reuses existing checkers used by pid1, so handling of aliases
should be consistent. Hopefully, with the test it'll be clearer what it
happening.

Support for .wants/.requires "aliases" is restored. Those are still used in the
wild quite a bit, so we need to support them.

See https://github.com/systemd/systemd/pull/13119 for a discussion of aliases
with an instance that point to a different template: this is allowed.
2020-01-10 14:27:04 +01:00
Zbigniew Jędrzejewski-Szmek 9a4f9e69e1 shared/unit-file: expose function to check .wants/.requires symlink validity
No functional change.
2020-01-10 12:20:18 +01:00
Kevin Kuehler a602a0b44b man: Document systemctl --with-dependencies switch 2020-01-09 22:58:00 -08:00
Kevin Kuehler e9c387c829 systemctl: Add --with-dependencies flag
Will print a unit and all of its dependencies. Works with cat, status,
list-units, and list-unit-files. This flag can also be used in conjunction
with --reverse, --before, and --after.

We also vastly simplify the list_dependencies_get_dependencies logic.
Instead of using 5 strvs and merging them into one, use one strv and
have the bus append all the map values to it.

Fixes #9273
2020-01-09 22:52:04 -08:00
Lennart Poettering 7cce68e1e0 core: make sure we use the correct mount flag when re-mounting bind mounts
When in a userns environment we cannot take away per-mount point flags
set on a mount point that was passed to us. Hence we need to be careful
to always check the actual mount flags in place and manipulate only
those flags of them that we actually want to change and not reset more
as side-effect.

We mostly got this right already in
bind_remount_recursive_with_mountinfo(), but didn't in the simpler
bind_remount_one_with_mountinfo(). Catch up.

(The old code assumed that the MountEntry.flags field contained the
right flag settings, but it actually doesn't for new mounts we just
established as for those mount() establishes the initial flags for us,
and we have to read them back to figure out which ones the kernel
picked.)

Fixes: #13622
2020-01-09 15:18:08 +01:00
Lennart Poettering 8403219fc1 mount-util: line break overly long function prototypes 2020-01-09 15:05:55 +01:00
Lennart Poettering 08b1f5c7d1 mount-util: clean up get_mount_flags()
This cleans up the function in multiple ways:

- change order of parameters to follow our usualy system of putting
  return parameters last
- rename return parameter "ret" as we usually do
- don't initialize local variables we override immediately anyway
- downgrade log messages to LOG_DEBUG (since we don't log about any
  other errors here above LOG_DEBUG, as this is mostly an "API"-style
  function)
- handle that mnt_fs_get_vfs_options() may return NULL (according to
  docs)
- manually map the ST_xyz to MS_xyz flags on statvfs(), because while
  they are mostly the same, they aren't entirely the same, MS_RELATIME and
  ST_RELATIME are defined differently (sad!)
2020-01-09 15:05:21 +01:00
Lennart Poettering 4eaf0d9401 mount-util: don't mask away MS_RDONLY twice
We have the flags mask for that, and if callers really wanted us to mask
this away, then they should pass the correct mask.
2020-01-09 14:55:15 +01:00
Lennart Poettering f3dab34d22 mount-util: rename cleaned → simplified, because that's what we actually did here 2020-01-09 14:53:36 +01:00
Kevin Kuehler 411975ce63 shared/bus-util: Don't replace exsting strv
Change the behavior of string arrays in a bus property map.  Previously,
passing the same strv pointer to more than one map entry would result in
the old strv being freed and overwritten. With this change, an existing
strv pointer is appended to.

This is important if we want to create one strv comprised of multiple
dependencies. This makes it so callers don't have to create one strv per
dependency and subsequently merge them into one strv.
2020-01-07 18:48:50 -08:00
Zbigniew Jędrzejewski-Szmek b59817b199 shared/install: drop creation of alias for DefaultInstance
It turns out that this is not necessary. When we try to resolve alias@inst, we
first check alias@inst, and if that is not found, fall back to alias@. Since we
already created a symlink for alias@, we will find that and the result will be
the same.
2019-12-21 12:39:06 +01:00
Zbigniew Jędrzejewski-Szmek f9ef25a483 basic/unit-name: make sure UnitNameFlags is signed
Without that, a check like unit_name_to_instance(...) < 0 would not
have the expected effect.
2019-12-19 20:57:27 +01:00
Zbigniew Jędrzejewski-Szmek aa0f357fd8 shared/install: split out alias verification function
No functional change.
2019-12-19 15:07:19 +01:00
Jérémy Rosen 3d0205f28b Be more strict about what can be an Alias for template and instances
* Templates can only use other templates as alisases
* Template instances can use templates or things that expand with an
  instance name
2019-12-18 11:00:49 +01:00
63 changed files with 1810 additions and 947 deletions

73
docs/INITRD_INTERFACE.md Normal file
View File

@ -0,0 +1,73 @@
---
title: Initrd Interface
category: Interfaces
layout: default
---
# The initrd Interface of systemd
The Linux initrd mechanism (short for "initial RAM disk") refers to a small
file system archive that is unpacked by the kernel and contains the first
userspace code that runs. It typically finds and transitions into the actual
root file system to use. systemd supports both initrd and initrd-less boots. If
an initrd is used it is a good idea to pass a few bits of runtime information
from the initrd to systemd in order to avoid duplicate work and to provide
performance data to the administrator. In this page we attempt to roughly
describe the interfaces that exist between the initrd and systemd. These
interfaces are currently used by dracut and the ArchLinux initrds.
* The initrd should mount `/run/` as a tmpfs and pass it pre-mounted when
jumping into the main system when executing systemd. The mount options should
be `mode=755,nodev,nosuid,strictatime`
* It's highly recommended that the initrd also mounts `/usr/` (if split off) as
appropriate and passes it pre-mounted to the main system, to avoid the
problems described in [Booting without /usr is
Broken](http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken).
* If the executable `/run/initramfs/shutdown` exists systemd will use it to
jump back into the initrd on shutdown. `/run/initramfs/` should be a usable
initrd environment to which systemd will pivot back and the `shutdown`
executable in it should be able to detach all complex storage that for
example was needed to mount the root file system. It's the job of the initrd
to set up this directory and executable in the right way so that this works
correctly. The shutdown binary is invoked with the shutdown verb as `argv[1]`,
optionally followed (in `argv[2]`, `argv[3]`, … systemd's original command
line options, for example `--log-level=` and similar.
* Storage daemons run from the initrd should follow the the guide on [systemd
and Storage Daemons for the Root File
System](https://systemd.io/ROOT_STORAGE_DAEMONS) to survive properly from the
boot initrd all the way to the point where systemd jumps back into the initrd
for shutdown.
One last clarification: we use the term _initrd_ very generically here
describing any kind of early boot file system, regardless whether that might be
implemented as an actual ramdisk, ramfs or tmpfs. We recommend using _initrd_
in this sense as a term that is unrelated to the actual backing technologies
used.
Oh, and one last question before closing: instead of implementing these
features in your own distro's initrd, may I suggest just using Dracut instead?
It's all already implemented there!
## Using systemd inside an initrd
It is also possible and recommended to implement the initrd itself based on
systemd. Here are a few terse notes:
* Provide `/etc/initrd-release` in the initrd image. The idea is that it follows
the same format as the usual `/etc/os-release` but describes the initial RAM
disk implementation rather than the OS. systemd uses the existence of this
file as a flag whether to run in initial RAM disk mode, or not.
* When run in initrd mode, systemd and its components will read a couple of
additional command line arguments, which are generally prefixed with `rd.`
* To transition into the main system image invoke `systemctl switch-root`.
* The switch-root operation will result in a killing spree of all running
processes. Some processes might need to be excluded from that, see the guide
on [systemd and Storage Daemons for the Root File
System](https://systemd.io/ROOT_STORAGE_DAEMONS).

View File

@ -11,20 +11,27 @@ systemd provides various interfaces developers and programs might rely on. Start
The stable interfaces are: The stable interfaces are:
* **The unit configuration file format**. Unit files written now will stay compatible with future versions of systemd. Extensions to the file format will happen in a way that existing files remain compatible. * **The unit configuration file format**. Unit files written now will stay compatible with future versions of systemd. Extensions to the file format will happen in a way that existing files remain compatible.
* **The command line interface** of `systemd`, `systemctl`, `loginctl`, `journalctl`, and all other command line utilities installed in `$PATH` and documented in a man page. We will make sure that scripts invoking these commands will continue to work with future versions of systemd. Note however that the output generated by these commands is generally not included in the promise, unless it is documented in the man page. Example: the output of `systemctl status` is not stable, but that of `systemctl show` is, because the former is intended to be human readable and the latter computer readable, and this is documented in the man page. * **The command line interface** of `systemd`, `systemctl`, `loginctl`, `journalctl`, and all other command line utilities installed in `$PATH` and documented in a man page. We will make sure that scripts invoking these commands will continue to work with future versions of systemd. Note however that the output generated by these commands is generally not included in the promise, unless it is documented in the man page. Example: the output of `systemctl status` is not stable, but that of `systemctl show` is, because the former is intended to be human readable and the latter computer readable, and this is documented in the man page.
* **The protocol spoken on the socket referred to by $NOTIFY_SOCKET**, as documented in (sd_notify(3))[https://www.freedesktop.org/software/systemd/man/sd_notify.html].
* **The protocol spoken on the socket referred to by `$NOTIFY_SOCKET`**, as documented in [sd_notify(3)](https://www.freedesktop.org/software/systemd/man/sd_notify.html).
* Some of the **"special" unit names** and their semantics. To be precise the ones that are necessary for normal services, and not those required only for early boot and late shutdown, with very few exceptions. To list them here: `basic.target`, `shutdown.target`, `sockets.target`, `network.target`, `getty.target`, `graphical.target`, `multi-user.target`, `rescue.target`, `emergency.target`, `poweroff.target`, `reboot.target`, `halt.target`, `runlevel[1-5].target`. * Some of the **"special" unit names** and their semantics. To be precise the ones that are necessary for normal services, and not those required only for early boot and late shutdown, with very few exceptions. To list them here: `basic.target`, `shutdown.target`, `sockets.target`, `network.target`, `getty.target`, `graphical.target`, `multi-user.target`, `rescue.target`, `emergency.target`, `poweroff.target`, `reboot.target`, `halt.target`, `runlevel[1-5].target`.
* **The D-Bus interfaces of the main service daemon and other daemons**. We try to always preserve backwards compatiblity, and intentational breakage is never introduced. Nevertheless, when we find bugs that mean that the existing interface was not useful, or when the implementation did something different than stated by the documentation and the implemented behaviour is not useful, we will fix the implementation and thus introduce a change in behaviour. But the API (parameter counts and types) is never changed, and existing attributes and methods will not be removed. * **The D-Bus interfaces of the main service daemon and other daemons**. We try to always preserve backwards compatiblity, and intentational breakage is never introduced. Nevertheless, when we find bugs that mean that the existing interface was not useful, or when the implementation did something different than stated by the documentation and the implemented behaviour is not useful, we will fix the implementation and thus introduce a change in behaviour. But the API (parameter counts and types) is never changed, and existing attributes and methods will not be removed.
* For a more comprehensive and authoritative list, consult the chart below. * For a more comprehensive and authoritative list, consult the chart below.
The following interfaces will not necessarily be kept stable for now, but we will eventually make a stability promise for these interfaces too. In the meantime we will however try to keep breakage of these interfaces at a minimum: The following interfaces will not necessarily be kept stable for now, but we will eventually make a stability promise for these interfaces too. In the meantime we will however try to keep breakage of these interfaces at a minimum:
* **The set of states of the various state machines used in systemd**, e.g. the high-level unit states inactive, active, deactivating, and so on, as well (and in particular) the low-level per-unit states. * **The set of states of the various state machines used in systemd**, e.g. the high-level unit states inactive, active, deactivating, and so on, as well (and in particular) the low-level per-unit states.
* **All "special" units that aren't listed above**. * **All "special" units that aren't listed above**.
The following interfaces are considered private to systemd, and are not and will not be covered by any stability promise: The following interfaces are considered private to systemd, and are not and will not be covered by any stability promise:
* **Undocumented switches** to `systemd`, `systemctl` and otherwise. * **Undocumented switches** to `systemd`, `systemctl` and otherwise.
* **The internal protocols** used on the various sockets such as the sockets `/run/systemd/shutdown`, `/run/systemd/private`. * **The internal protocols** used on the various sockets such as the sockets `/run/systemd/shutdown`, `/run/systemd/private`.
One of the main goals of systemd is to unify basic Linux configurations and service behaviors across all distributions. Systemd project does not contain any distribution-specific parts. Distributions are expected to convert over time their individual configurations to the systemd format, or they will need to carry and maintain patches in their package if they still decide to stay different. One of the main goals of systemd is to unify basic Linux configurations and service behaviors across all distributions. Systemd project does not contain any distribution-specific parts. Distributions are expected to convert over time their individual configurations to the systemd format, or they will need to carry and maintain patches in their package if they still decide to stay different.
@ -77,19 +84,19 @@ And now, here's the list of (hopefully) all APIs that we have introduced with sy
| [hostnamed](https://www.freedesktop.org/wiki/Software/systemd/hostnamed) | D-Bus | yes | yes | GNOME | yes | [Ubuntu](https://launchpad.net/ubuntu/+source/ubuntu-system-service), [Gentoo](http://www.gentoo.org/proj/en/desktop/gnome/openrc-settingsd.xml), [BSD](http://uglyman.kremlin.cc/gitweb/gitweb.cgi?p=systembsd.git;a=summary) | partially | | [hostnamed](https://www.freedesktop.org/wiki/Software/systemd/hostnamed) | D-Bus | yes | yes | GNOME | yes | [Ubuntu](https://launchpad.net/ubuntu/+source/ubuntu-system-service), [Gentoo](http://www.gentoo.org/proj/en/desktop/gnome/openrc-settingsd.xml), [BSD](http://uglyman.kremlin.cc/gitweb/gitweb.cgi?p=systembsd.git;a=summary) | partially |
| [localed](https://www.freedesktop.org/wiki/Software/systemd/localed) | D-Bus | yes | yes | GNOME | yes | [Ubuntu](https://launchpad.net/ubuntu/+source/ubuntu-system-service), [Gentoo](http://www.gentoo.org/proj/en/desktop/gnome/openrc-settingsd.xml), [BSD](http://uglyman.kremlin.cc/gitweb/gitweb.cgi?p=systembsd.git;a=summary) | partially | | [localed](https://www.freedesktop.org/wiki/Software/systemd/localed) | D-Bus | yes | yes | GNOME | yes | [Ubuntu](https://launchpad.net/ubuntu/+source/ubuntu-system-service), [Gentoo](http://www.gentoo.org/proj/en/desktop/gnome/openrc-settingsd.xml), [BSD](http://uglyman.kremlin.cc/gitweb/gitweb.cgi?p=systembsd.git;a=summary) | partially |
| [timedated](https://www.freedesktop.org/wiki/Software/systemd/timedated) | D-Bus | yes | yes | GNOME | yes | [Gentoo](http://www.gentoo.org/proj/en/desktop/gnome/openrc-settingsd.xml), [BSD](http://uglyman.kremlin.cc/gitweb/gitweb.cgi?p=systembsd.git;a=summary) | partially | | [timedated](https://www.freedesktop.org/wiki/Software/systemd/timedated) | D-Bus | yes | yes | GNOME | yes | [Gentoo](http://www.gentoo.org/proj/en/desktop/gnome/openrc-settingsd.xml), [BSD](http://uglyman.kremlin.cc/gitweb/gitweb.cgi?p=systembsd.git;a=summary) | partially |
| [initrd interface](https://www.freedesktop.org/wiki/Software/systemd/InitrdInterface) | Environment, flag files | yes | yes | dracut, [ArchLinux](ArchLinux) | yes | [ArchLinux](ArchLinux) | no | | [initrd interface](https://www.freedesktop.org/wiki/Software/systemd/InitrdInterface) | Environment, flag files | yes | yes | dracut, ArchLinux | yes | ArchLinux | no |
| [Container interface](https://www.freedesktop.org/wiki/Software/systemd/ContainerInterface) | Environment, Mounts | yes | yes | libvirt/LXC | yes | - | no | | [Container interface](https://systemd.io/CONTAINER_INTERFACE) | Environment, Mounts | yes | yes | libvirt/LXC | yes | - | no |
| [Boot Loader interface](https://systemd.io/BOOT_LOADER_INTERFACE) | EFI variables | yes | yes | gummiboot | yes | - | no | | [Boot Loader interface](https://systemd.io/BOOT_LOADER_INTERFACE) | EFI variables | yes | yes | gummiboot | yes | - | no |
| [Service bus API](https://www.freedesktop.org/wiki/Software/systemd/dbus) | D-Bus | yes | yes | system-config-services | no | - | no | | [Service bus API](https://www.freedesktop.org/wiki/Software/systemd/dbus) | D-Bus | yes | yes | system-config-services | no | - | no |
| [logind](https://www.freedesktop.org/wiki/Software/systemd/logind) | D-Bus | yes | yes | GNOME | no | - | no | | [logind](https://www.freedesktop.org/wiki/Software/systemd/logind) | D-Bus | yes | yes | GNOME | no | - | no |
| [sd-login.h API](https://www.freedesktop.org/software/systemd/man/sd-login.html) | C Library | yes | yes | GNOME, [PolicyKit](PolicyKit), ... | no | - | no | | [sd-login.h API](https://www.freedesktop.org/software/systemd/man/sd-login.html) | C Library | yes | yes | GNOME, PolicyKit, ... | no | - | no |
| [sd-daemon.h API](https://www.freedesktop.org/software/systemd/man/sd-daemon.html) | C Library or Drop-in | yes | yes | numerous | yes | - | yes | | [sd-daemon.h API](https://www.freedesktop.org/software/systemd/man/sd-daemon.html) | C Library or Drop-in | yes | yes | numerous | yes | - | yes |
| [sd-id128.h API](https://www.freedesktop.org/software/systemd/man/sd-id128.html) | C Library | yes | yes | - | yes | - | no | | [sd-id128.h API](https://www.freedesktop.org/software/systemd/man/sd-id128.html) | C Library | yes | yes | - | yes | - | no |
| [sd-journal.h API](https://www.freedesktop.org/software/systemd/man/sd-journal.html) | C Library | yes | yes | - | maybe | - | no | | [sd-journal.h API](https://www.freedesktop.org/software/systemd/man/sd-journal.html) | C Library | yes | yes | - | maybe | - | no |
| [$XDG_RUNTIME_DIR](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) | Environment | yes | yes | glib, GNOME | yes | - | no | | [$XDG_RUNTIME_DIR](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) | Environment | yes | yes | glib, GNOME | yes | - | no |
| [$LISTEN_FDS $LISTEN_PID FD Passing](https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html) | Environment | yes | yes | numerous (via sd-daemon.h) | yes | - | no | | [$LISTEN_FDS $LISTEN_PID FD Passing](https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html) | Environment | yes | yes | numerous (via sd-daemon.h) | yes | - | no |
| [$NOTIFY_SOCKET Daemon Notifications](https://www.freedesktop.org/software/systemd/man/sd_notify.html) | Environment | yes | yes | a few, including udev | yes | - | no | | [$NOTIFY_SOCKET Daemon Notifications](https://www.freedesktop.org/software/systemd/man/sd_notify.html) | Environment | yes | yes | a few, including udev | yes | - | no |
| [argv&#91;0&#93;&#91;0&#93;='@' Logic](https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons) | /proc marking | yes | yes | mdadm | yes | - | no | | [argv&#91;0&#93;&#91;0&#93;='@' Logic](https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons) | `/proc` marking | yes | yes | mdadm | yes | - | no |
| [Unit file format](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) | File format | yes | yes | numerous | no | - | no | | [Unit file format](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) | File format | yes | yes | numerous | no | - | no |
| [Journal File Format](https://www.freedesktop.org/wiki/Software/systemd/journal-files) | File format | yes | yes | - | maybe | - | no | | [Journal File Format](https://www.freedesktop.org/wiki/Software/systemd/journal-files) | File format | yes | yes | - | maybe | - | no |
| [Journal Export Format](https://www.freedesktop.org/wiki/Software/systemd/export) | File format | yes | yes | - | yes | - | no | | [Journal Export Format](https://www.freedesktop.org/wiki/Software/systemd/export) | File format | yes | yes | - | yes | - | no |
@ -98,19 +105,19 @@ And now, here's the list of (hopefully) all APIs that we have introduced with sy
| [udev multi-seat properties](https://www.freedesktop.org/wiki/Software/systemd/multiseat) | udev Property | yes | yes | X11, gdm | no | - | no | | [udev multi-seat properties](https://www.freedesktop.org/wiki/Software/systemd/multiseat) | udev Property | yes | yes | X11, gdm | no | - | no |
| udev session switch ACL properties | udev Property | no | no | - | no | - | no | | udev session switch ACL properties | udev Property | no | no | - | no | - | no |
| [CLI of systemctl,...](https://www.freedesktop.org/software/systemd/man/systemctl.html) | CLI | yes | yes | numerous | no | - | no | | [CLI of systemctl,...](https://www.freedesktop.org/software/systemd/man/systemctl.html) | CLI | yes | yes | numerous | no | - | no |
| [tmpfiles.d](https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html) | File format | yes | yes | numerous | yes | [ArchLinux](ArchLinux) | partially | | [tmpfiles.d](https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html) | File format | yes | yes | numerous | yes | ArchLinux | partially |
| [sysusers.d](https://www.freedesktop.org/software/systemd/man/sysusers.d.html) | File format | yes | yes | unknown | yes | | partially | | [sysusers.d](https://www.freedesktop.org/software/systemd/man/sysusers.d.html) | File format | yes | yes | unknown | yes | | partially |
| [/etc/machine-id](https://www.freedesktop.org/software/systemd/man/machine-id.html) | File format | yes | yes | D-Bus | yes | - | no | | [/etc/machine-id](https://www.freedesktop.org/software/systemd/man/machine-id.html) | File format | yes | yes | D-Bus | yes | - | no |
| [binfmt.d](https://www.freedesktop.org/software/systemd/man/binfmt.d.html) | File format | yes | yes | numerous | yes | - | partially | | [binfmt.d](https://www.freedesktop.org/software/systemd/man/binfmt.d.html) | File format | yes | yes | numerous | yes | - | partially |
| [/etc/hostname](https://www.freedesktop.org/software/systemd/man/hostname.html) | File format | yes | yes | numerous (it's a Debian thing) | yes | Debian, [ArchLinux](ArchLinux) | no | | [/etc/hostname](https://www.freedesktop.org/software/systemd/man/hostname.html) | File format | yes | yes | numerous (it's a Debian thing) | yes | Debian, ArchLinux | no |
| [/etc/locale.conf](https://www.freedesktop.org/software/systemd/man/locale.conf.html) | File format | yes | yes | - | yes | [ArchLinux](ArchLinux) | partially | | [/etc/locale.conf](https://www.freedesktop.org/software/systemd/man/locale.conf.html) | File format | yes | yes | - | yes | ArchLinux | partially |
| [/etc/machine-info](https://www.freedesktop.org/software/systemd/man/machine-info.html) | File format | yes | yes | - | yes | - | partially | | [/etc/machine-info](https://www.freedesktop.org/software/systemd/man/machine-info.html) | File format | yes | yes | - | yes | - | partially |
| [modules-load.d](https://www.freedesktop.org/software/systemd/man/modules-load.d.html) | File format | yes | yes | numerous | yes | - | partially | | [modules-load.d](https://www.freedesktop.org/software/systemd/man/modules-load.d.html) | File format | yes | yes | numerous | yes | - | partially |
| [/usr/lib/os-release](https://www.freedesktop.org/software/systemd/man/os-release.html) | File format | yes | yes | some | yes | Fedora, OpenSUSE, [ArchLinux](ArchLinux), Angstrom, Frugalware, others... | no | | [/usr/lib/os-release](https://www.freedesktop.org/software/systemd/man/os-release.html) | File format | yes | yes | some | yes | Fedora, OpenSUSE, ArchLinux, Angstrom, Frugalware, others... | no |
| [sysctl.d](https://www.freedesktop.org/software/systemd/man/sysctl.d.html) | File format | yes | yes | some (it's a Debian thing) | yes | procps/Debian, [ArchLinux](ArchLinux) | partially | | [sysctl.d](https://www.freedesktop.org/software/systemd/man/sysctl.d.html) | File format | yes | yes | some (it's a Debian thing) | yes | procps/Debian, ArchLinux | partially |
| [/etc/timezone](https://www.freedesktop.org/software/systemd/man/timezone.html) | File format | yes | yes | numerous (it's a Debian thing) | yes | Debian | partially | | [/etc/timezone](https://www.freedesktop.org/software/systemd/man/timezone.html) | File format | yes | yes | numerous (it's a Debian thing) | yes | Debian | partially |
| [/etc/vconsole.conf](https://www.freedesktop.org/software/systemd/man/vconsole.conf.html) | File format | yes | yes | - | yes | [ArchLinux](ArchLinux) | partially | | [/etc/vconsole.conf](https://www.freedesktop.org/software/systemd/man/vconsole.conf.html) | File format | yes | yes | - | yes | ArchLinux | partially |
| /run | File hierarchy change | yes | yes | numerous | yes | OpenSUSE, Debian, [ArchLinux](ArchLinux) | no | | `/run` | File hierarchy change | yes | yes | numerous | yes | OpenSUSE, Debian, ArchLinux | no |
| [Generators](https://www.freedesktop.org/software/systemd/man/systemd.generator.html) | Subprocess | yes | yes | - | no | - | no | | [Generators](https://www.freedesktop.org/software/systemd/man/systemd.generator.html) | Subprocess | yes | yes | - | no | - | no |
| [System Updates](https://www.freedesktop.org/software/systemd/man/systemd.offline-updates.html) | System Mode | yes | yes | - | no | - | no | | [System Updates](https://www.freedesktop.org/software/systemd/man/systemd.offline-updates.html) | System Mode | yes | yes | - | no | - | no |
| [Presets](https://freedesktop.org/wiki/Software/systemd/Preset) | File format | yes | yes | - | no | - | no | | [Presets](https://freedesktop.org/wiki/Software/systemd/Preset) | File format | yes | yes | - | no | - | no |

View File

@ -0,0 +1,193 @@
---
title: Storage Daemons for the Root File System
category: Interfaces
layout: default
---
# systemd and Storage Daemons for the Root File System
a.k.a. _Pax Cellae pro Radix Arbor_
(or something like that, my Latin is a bit rusty)
A number of complex storage technologies on Linux (e.g. RAID, volume
management, networked storage) require user space services to run while the
storage is active and mountable. This requirement becomes tricky as soon as the
root file system of the Linux operating system is stored on such storage
technology. Previously no clear path to make this work was available. This text
tries to clear up the resulting confusion, and what is now supported and what
is not.
## A Bit of Background
When complex storage technologies are used as backing for the root file system
this needs to be set up by the initial RAM file system (initrd), i.e. on Fedora
by Dracut. In newer systemd versions tear-down of the root file system backing
is also done by the initrd: after terminating all remaining running processes
and unmounting all file systems it can (which means excluding the root fs)
systemd will jump back into the initrd code allowing it to unmount the final
file systems (and its storage backing) that could not be unmounted as long as
the OS was still running from the main root file system. The initrd' job is to
detach/unmount the root fs, i.e. inverting the exact commands it used to set
them up in the first place. This is not only cleaner, but also allows for the
first time arbitrary complex stacks of storage technology.
Previous attempts to handle root file system setups with complex storage as
backing usually tried to maintain the root storage with program code stored on
the root storage itself, thus creating a number of dependency loops. Safely
detaching such a root file system becomes messy, since the program code on the
storage needs to stay around longer than the storage, which is technically
contradicting.
## What's new?
As a result, we hereby clarify that we do not support storage technology setups
where the storage daemons are being run from the storage it maintains
itself. In other words: a storage daemon backing the root file system cannot be
stored on the root file system itself.
What we do support instead is that these storage daemons are started from the
initrd, stay running all the time during normal operation and are terminated
only after we returned control back to the initrd and by the initrd. As such,
storage daemons involved with maintaining the root file system storage
conceptually are more like kernel threads than like normal system services:
from the perspective of the init system (i.e. systemd) these services have been
started before systemd got initialized and stay around until after systemd is
already gone. These daemons can only be updated by updating the initrd and
rebooting, a takeover from initrd-supplied services to replacements from the
root file system is not supported.
## What does this mean?
Near the end of system shutdown, systemd executes a small tool called
systemd-shutdown, replacing its own process. This tool (which runs as PID 1, as
it entirely replaces the systemd init process) then iterates through the
mounted file systems and running processes (as well as a couple of other
resources) and tries to unmount/read-only mount/detach/kill them. It continues
to do this in a tight loop as long as this results in any effect. From this
killing spree a couple of processes are automatically excluded: PID 1 itself of
course, as well as all kernel threads. After the killing/unmounting spree
control is passed back to the initrd, whose job is then to unmount/detach
whatever might be remaining.
The same killing spree logic (but not the unmount/detach/read-only logic) is
applied during the transition from the initrd to the main system (i.e. the
"`switch_root`" operation), so that no processes from the initrd survive to the
main system.
To implement the supported logic proposed above (i.e. where storage daemons
needed for the root fs which are started by the initrd stay around during
normal operation and are only killed after control is passed back to the
initrd) we need to exclude these daemons from the shutdown/switch_root killing
spree. To accomplish this the following logic is available starting with
systemd 38:
Processes (run by the root user) whose first character of the zeroth command
line argument is `@` are excluded from the killing spree, much the same way as
kernel threads are excluded too. Thus, a daemon which wants to take advantage
of this logic needs to place the following at the top of its main() function:
```c
[0][0] = '@';
```
And that's already it. Note that this functionality is only to be used by
programs running from the initrd, and **not** for programs running from the
root file system itself. Programs which use this functionality and are running
from the root file system are considered buggy since they effectively prohibit
clean unmounting/detaching of the root file system and its backing storage.
_Again: if your code is being run from the root file system, then this logic
suggested above is **NOT** for you. Sorry. Talk to us, we can probably help you
to find a different solution to your problem._
The recommended way to distinguish between run-from-initrd and run-from-rootfs
for a daemon is to check for `/etc/initrd-release` (which exists on all modern
initrd implementations, see the [initrd
Interface](http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface)
for details) which when exists results in `argv[0][0]` being set to `@`, and
otherwise doesn't. Something like this:
```c
#include <unistd.h>
int main(int argc, char *argv[]) {
if (access("/etc/initrd-release", F_OK) >= 0)
argv[0][0] = '@';
}
```
Why `@`? Why `argv[0][0]`? First of all, a technique like this is not without
precedent: traditionally Unix login shells set `argv[0][0]` to `-` to clarify
they are login shells. This logic is also very easy to implement. We have been
looking for other ways to mark processes for exclusion from the killing spree,
but could not find any that was equally simple to implement and quick to read
when traversing through `/proc/`. Also, as a side effect replacing the first
character of `argv[0]` with `@` also visually invalidates the path normally
stored in `argv[0]` (which usually starts with `/`) thus helping the
administrator to understand that your daemon is actually not originating from
the actual root file system, but from a path in a completely different
namespace (i.e. the initrd namespace). Other than that we just think that `@`
is a cool character which looks pretty in the ps output... 😎
Note that your code should only modify `argv[0][0]` and leave the comm name
(i.e. `/proc/self/comm`) of your process untouched.
## To which technologies does this apply?
These recommendations apply to those storage daemons which need to stay around
until after the storage they maintain is unmounted. If your storage daemon is
fine with being shut down before its storage device is unmounted you may ignore
the recommendations above.
This all applies to storage technology only, not to daemons with any other
(non-storage related) purposes.
## What else to keep in mind?
If your daemon implements the logic pointed out above it should work nicely
from initrd environments. In many cases it might be necessary to additionally
support storage daemons to be started from within the actual OS, for example
when complex storage setups are used for auxiliary file systems, i.e. not the
root file system, or created by the administrator during runtime. Here are a
few additional notes for supporting these setups:
* If your storage daemon is run from the main OS (i.e. not the initrd) it will
also be terminated when the OS shuts down (i.e. before we pass control back
to the initrd). Your daemon needs to handle this properly.
* It is not acceptable to spawn off background processes transparently from
user commands or udev rules. Whenever a process is forked off on Unix it
inherits a multitude of process attributes (ranging from the obvious to the
not-so-obvious such as security contexts or audit trails) from its parent
process. It is practically impossible to fully detach a service from the
process context of the spawning process. In particular, systemd tracks which
processes belong to a service or login sessions very closely, and by spawning
off your storage daemon from udev or an administrator command you thus make
it part of its service/login. Effectively this means that whenever udev is
shut down, your storage daemon is killed too, resp. whenever the login
session goes away your storage might be terminated as well. (Also note that
recent udev versions will automatically kill all long running background
processes forked off udev rules now.) So, in summary: double-forking off
processes from user commands or udev rules is **NOT** OK!
* To automatically spawn storage daemons from udev rules or administrator
commands, the recommended technology is socket-based activation as
implemented by systemd. Transparently for your client code connecting to the
socket of your storage daemon will result in the storage to be started. For
that it is simply necessary to inform systemd about the socket you'd like it
to listen on on behalf of your daemon and minimally modify the daemon to
receive the listening socket for its services from systemd instead of
creating it on its own. Such modifications can be minimal, and are easily
written in a way that does not negatively impact usability on non-systemd
systems. For more information on making use of socket activation in your
program consult this blog story: [Socket
Activation](http://0pointer.de/blog/projects/socket-activation.html)
* Consider having a look at the [initrd Interface of systemd](http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface)

View File

@ -479,6 +479,13 @@ evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnLENOVO:*pvrLenovoideapadY700-14ISK*
EVDEV_ABS_35=::27 EVDEV_ABS_35=::27
EVDEV_ABS_36=::29 EVDEV_ABS_36=::29
# Lenovo Ideapad 310S-14ISK
evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnLENOVO:*pvrLenovoideapad310S-14ISK*
EVDEV_ABS_00=113:3960:37
EVDEV_ABS_01=100:1959:27
EVDEV_ABS_35=113:3960:37
EVDEV_ABS_36=100:1959:27
# Lenovo Ideapad 500S-13ISK # Lenovo Ideapad 500S-13ISK
evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnLENOVO:*pvrLenovoideapad500S-13ISK* evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnLENOVO:*pvrLenovoideapad500S-13ISK*
EVDEV_ABS_00=125:3955:37 EVDEV_ABS_00=125:3955:37

View File

@ -1592,6 +1592,22 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--with-dependencies</option></term>
<listitem>
<para>When used with <command>status</command>,
<command>cat</command>, <command>list-units</command>, and
<command>list-unit-files</command>, those commands print all
specified units and the dependencies of those units.</para>
<para>Options <option>--reverse</option>,
<option>--after</option>, <option>--before</option>
may be used to change what types of dependencies
are shown.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>-l</option></term> <term><option>-l</option></term>
<term><option>--full</option></term> <term><option>--full</option></term>

View File

@ -422,22 +422,24 @@
<varlistentry> <varlistentry>
<term><varname>What=</varname></term> <term><varname>What=</varname></term>
<listitem><para>Takes an absolute path of a device node, file or other resource to mount. See <citerefentry <listitem><para>Takes an absolute path of a device node, file or other resource to mount. See
project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> for details. If <citerefentry
this refers to a device node, a dependency on the respective device unit is automatically created. (See project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
<citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more details. If this refers to a device node, a dependency on the respective device unit is automatically
information.) This option is mandatory. Note that the usual specifier expansion is applied to this setting, created. (See
literal percent characters should hence be written as <literal>%%</literal>.</para></listitem> <citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for more information.) This option is mandatory. Note that the usual specifier expansion is applied
to this setting, literal percent characters should hence be written as <literal>%%</literal>. If this
mount is a bind mount and the specified path does not exist yet it is created as
directory.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>Where=</varname></term> <term><varname>Where=</varname></term>
<listitem><para>Takes an absolute path of a directory for the <listitem><para>Takes an absolute path of a file or directory for the mount point; in particular, the
mount point; in particular, the destination cannot be a symbolic destination cannot be a symbolic link. If the mount point does not exist at the time of mounting, it
link. If the mount point does not exist at the time of is created as directory. This string must be reflected in the unit filename. (See above.) This option
mounting, it is created. This string must be reflected in the is mandatory.</para></listitem>
unit filename. (See above.) This option is
mandatory.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -136,11 +136,25 @@
has the alias <filename>dbus-org.freedesktop.network1.service</filename>, created during installation as has the alias <filename>dbus-org.freedesktop.network1.service</filename>, created during installation as
a symlink, so when <command>systemd</command> is asked through D-Bus to load a symlink, so when <command>systemd</command> is asked through D-Bus to load
<filename>dbus-org.freedesktop.network1.service</filename>, it'll load <filename>dbus-org.freedesktop.network1.service</filename>, it'll load
<filename>systemd-networkd.service</filename>. Alias names may be used in commands like <filename>systemd-networkd.service</filename>. As another example, <filename>default.target</filename>
<command>disable</command>, <command>start</command>, <command>stop</command>, <command>status</command>, the default system target started at boot — is commonly symlinked (aliased) to either
and similar, and in all unit dependency directives, including <varname>Wants=</varname>, <filename>multi-user.target</filename> or <filename>graphical.target</filename> to select what is started
<varname>Requires=</varname>, <varname>Before=</varname>, <varname>After=</varname>. Aliases cannot be by default. Alias names may be used in commands like <command>disable</command>,
used with the <command>preset</command> command.</para> <command>start</command>, <command>stop</command>, <command>status</command>, and similar, and in all
unit dependency directives, including <varname>Wants=</varname>, <varname>Requires=</varname>,
<varname>Before=</varname>, <varname>After=</varname>. Aliases cannot be used with the
<command>preset</command> command.</para>
<para>Aliases obey the following restrictions: a unit of a certain type (<literal>.service</literal>,
<literal>.socket</literal>, …) can only be aliased by a name with the same type suffix. A plain unit (not
a template or an instance), may only be aliased by a plain name. A template instance may only be aliased
by another template instance, and the instance part must be identical. A template may be aliased by
another template (in which case the alias applies to all instances of the template). As a special case, a
template instance (e.g. <literal>alias@inst.service</literal>) may be a symlink to different template
(e.g. <literal>template@inst.service</literal>). In that case, just this specific instance is aliased,
while other instances of the template (e.g. <literal>alias@foo.service</literal>,
<literal>alias@bar.service</literal>) are not aliased. Those rule preserve the requirement that the
instance (if any) is always uniquely defined for a given unit and all its aliases.</para>
<para>Unit files may specify aliases through the <varname>Alias=</varname> directive in the [Install] <para>Unit files may specify aliases through the <varname>Alias=</varname> directive in the [Install]
section. When the unit is enabled, symlinks will be created for those names, and removed when the unit is section. When the unit is enabled, symlinks will be created for those names, and removed when the unit is
@ -184,6 +198,16 @@
i.e. <filename>foo-bar-.service.d/10-override.conf</filename> overrides i.e. <filename>foo-bar-.service.d/10-override.conf</filename> overrides
<filename>foo-.service.d/10-override.conf</filename>.</para> <filename>foo-.service.d/10-override.conf</filename>.</para>
<para>In cases of unit aliases (described above), dropins for the aliased name and all aliases are
loaded. In the example of <filename>default.target</filename> aliasing
<filename>graphical.target</filename>, <filename>default.target.d/</filename>,
<filename>default.target.wants/</filename>, <filename>default.target.requires/</filename>,
<filename>graphical.target.d/</filename>, <filename>graphical.target.wants/</filename>,
<filename>graphical.target.requires/</filename> would all be read. For templates, dropins for the
template, any template aliases, the template instance, and all alias instances are read. When just a
specific template instance is aliased, then the dropins for the target template, the target template
instance, and the alias template instance are read.</para>
<para>In addition to <filename>/etc/systemd/system</filename>, the drop-in <literal>.d/</literal> <para>In addition to <filename>/etc/systemd/system</filename>, the drop-in <literal>.d/</literal>
directories for system services can be placed in <filename>/usr/lib/systemd/system</filename> or directories for system services can be placed in <filename>/usr/lib/systemd/system</filename> or
<filename>/run/systemd/system</filename> directories. Drop-in files in <filename>/etc</filename> <filename>/run/systemd/system</filename> directories. Drop-in files in <filename>/etc</filename>

View File

@ -17,6 +17,7 @@
#include "pretty-print.h" #include "pretty-print.h"
#include "process-util.h" #include "process-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "socket-netlink.h"
#include "socket-util.h" #include "socket-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"

View File

@ -439,52 +439,6 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union
return -EINVAL; return -EINVAL;
} }
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
_cleanup_free_ char *buf = NULL;
const char *suffix;
int r, ifi = 0;
assert(s);
assert(family);
assert(ret);
/* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
* if one is found. */
suffix = strchr(s, '%');
if (suffix) {
if (ifindex) {
/* If we shall return the interface index, try to parse it */
r = parse_ifindex(suffix + 1, &ifi);
if (r < 0) {
unsigned u;
u = if_nametoindex(suffix + 1);
if (u <= 0)
return -errno;
ifi = (int) u;
}
}
buf = strndup(s, suffix - s);
if (!buf)
return -ENOMEM;
s = buf;
}
r = in_addr_from_string_auto(s, family, ret);
if (r < 0)
return r;
if (ifindex)
*ifindex = ifi;
return r;
}
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
assert(addr); assert(addr);

View File

@ -42,7 +42,7 @@ int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned
int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret);
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr); unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr);
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);

View File

@ -79,11 +79,10 @@ int parse_mode(const char *s, mode_t *ret) {
return 0; return 0;
} }
int parse_ifindex(const char *s, int *ret) { int parse_ifindex(const char *s) {
int ifi, r; int ifi, r;
assert(s); assert(s);
assert(ret);
r = safe_atoi(s, &ifi); r = safe_atoi(s, &ifi);
if (r < 0) if (r < 0)
@ -91,26 +90,7 @@ int parse_ifindex(const char *s, int *ret) {
if (ifi <= 0) if (ifi <= 0)
return -EINVAL; return -EINVAL;
*ret = ifi; return ifi;
return 0;
}
int parse_ifindex_or_ifname(const char *s, int *ret) {
int r;
assert(s);
assert(ret);
r = parse_ifindex(s, ret);
if (r >= 0)
return r;
r = (int) if_nametoindex(s);
if (r <= 0)
return -errno;
*ret = r;
return 0;
} }
int parse_mtu(int family, const char *s, uint32_t *ret) { int parse_mtu(int family, const char *s, uint32_t *ret) {

View File

@ -13,8 +13,7 @@ int parse_boolean(const char *v) _pure_;
int parse_dev(const char *s, dev_t *ret); int parse_dev(const char *s, dev_t *ret);
int parse_pid(const char *s, pid_t* ret_pid); int parse_pid(const char *s, pid_t* ret_pid);
int parse_mode(const char *s, mode_t *ret); int parse_mode(const char *s, mode_t *ret);
int parse_ifindex(const char *s, int *ret); int parse_ifindex(const char *s);
int parse_ifindex_or_ifname(const char *s, int *ret);
int parse_mtu(int family, const char *s, uint32_t *ret); int parse_mtu(int family, const char *s, uint32_t *ret);
int parse_size(const char *t, uint64_t base, uint64_t *size); int parse_size(const char *t, uint64_t base, uint64_t *size);

View File

@ -134,31 +134,3 @@ int socket_address_listen(
return r; return r;
} }
int make_socket_fd(int log_level, const char* address, int type, int flags) {
SocketAddress a;
int fd, r;
r = socket_address_parse(&a, address);
if (r < 0)
return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
a.type = type;
fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
NULL, false, false, false, 0755, 0644, NULL);
if (fd < 0 || log_get_max_level() >= log_level) {
_cleanup_free_ char *p = NULL;
r = socket_address_print(&a, &p);
if (r < 0)
return log_error_errno(r, "socket_address_print(): %m");
if (fd < 0)
log_error_errno(fd, "Failed to listen on %s: %m", p);
else
log_full(log_level, "Listening on %s", p);
}
return fd;
}

View File

@ -52,227 +52,6 @@ static const char* const socket_address_type_table[] = {
DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int);
int socket_address_parse(SocketAddress *a, const char *s) {
_cleanup_free_ char *n = NULL;
char *e;
int r;
assert(a);
assert(s);
*a = (SocketAddress) {
.type = SOCK_STREAM,
};
if (*s == '[') {
uint16_t port;
/* IPv6 in [x:.....:z]:p notation */
e = strchr(s+1, ']');
if (!e)
return -EINVAL;
n = strndup(s+1, e-s-1);
if (!n)
return -ENOMEM;
errno = 0;
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
return errno_or_else(EINVAL);
e++;
if (*e != ':')
return -EINVAL;
e++;
r = parse_ip_port(e, &port);
if (r < 0)
return r;
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htobe16(port);
a->size = sizeof(struct sockaddr_in6);
} else if (*s == '/') {
/* AF_UNIX socket */
size_t l;
l = strlen(s);
if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when
* parsing (the kernel itself is less strict here in what it
* accepts) */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
memcpy(a->sockaddr.un.sun_path, s, l);
a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
} else if (*s == '@') {
/* Abstract AF_UNIX socket */
size_t l;
l = strlen(s+1);
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated sockets here
* when parsing, even though abstract namespace sockets
* explicitly allow embedded NUL bytes and don't consider
* them special. But it's simply annoying to debug such
* sockets. */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
} else if (startswith(s, "vsock:")) {
/* AF_VSOCK socket in vsock:cid:port notation */
const char *cid_start = s + STRLEN("vsock:");
unsigned port;
e = strchr(cid_start, ':');
if (!e)
return -EINVAL;
r = safe_atou(e+1, &port);
if (r < 0)
return r;
n = strndup(cid_start, e - cid_start);
if (!n)
return -ENOMEM;
if (!isempty(n)) {
r = safe_atou(n, &a->sockaddr.vm.svm_cid);
if (r < 0)
return r;
} else
a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
a->sockaddr.vm.svm_family = AF_VSOCK;
a->sockaddr.vm.svm_port = port;
a->size = sizeof(struct sockaddr_vm);
} else {
uint16_t port;
e = strchr(s, ':');
if (e) {
r = parse_ip_port(e + 1, &port);
if (r < 0)
return r;
n = strndup(s, e-s);
if (!n)
return -ENOMEM;
/* IPv4 in w.x.y.z:p notation? */
r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
if (r < 0)
return -errno;
if (r > 0) {
/* Gotcha, it's a traditional IPv4 address */
a->sockaddr.in.sin_family = AF_INET;
a->sockaddr.in.sin_port = htobe16(port);
a->size = sizeof(struct sockaddr_in);
} else {
unsigned idx;
if (strlen(n) > IF_NAMESIZE-1)
return -EINVAL;
/* Uh, our last resort, an interface name */
idx = if_nametoindex(n);
if (idx == 0)
return -EINVAL;
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_scope_id = idx;
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
}
} else {
/* Just a port */
r = parse_ip_port(s, &port);
if (r < 0)
return r;
if (socket_ipv6_is_supported()) {
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
} else {
a->sockaddr.in.sin_family = AF_INET;
a->sockaddr.in.sin_port = htobe16(port);
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
a->size = sizeof(struct sockaddr_in);
}
}
}
return 0;
}
int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
SocketAddress b;
int r;
/* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
r = socket_address_parse(&b, s);
if (r < 0)
return r;
if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
return -EAFNOSUPPORT;
}
*a = b;
return 0;
}
int socket_address_parse_netlink(SocketAddress *a, const char *s) {
_cleanup_free_ char *word = NULL;
unsigned group = 0;
int family, r;
assert(a);
assert(s);
zero(*a);
a->type = SOCK_RAW;
r = extract_first_word(&s, &word, NULL, 0);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
family = netlink_family_from_string(word);
if (family < 0)
return -EINVAL;
if (!isempty(s)) {
r = safe_atou(s, &group);
if (r < 0)
return r;
}
a->sockaddr.nl.nl_family = AF_NETLINK;
a->sockaddr.nl.nl_groups = group;
a->type = SOCK_RAW;
a->size = sizeof(struct sockaddr_nl);
a->protocol = family;
return 0;
}
int socket_address_verify(const SocketAddress *a, bool strict) { int socket_address_verify(const SocketAddress *a, bool strict) {
assert(a); assert(a);
@ -482,32 +261,6 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
return true; return true;
} }
bool socket_address_is(const SocketAddress *a, const char *s, int type) {
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse(&b, s) < 0)
return false;
b.type = type;
return socket_address_equal(a, &b);
}
bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse_netlink(&b, s) < 0)
return false;
return socket_address_equal(a, &b);
}
const char* socket_address_get_path(const SocketAddress *a) { const char* socket_address_get_path(const SocketAddress *a) {
assert(a); assert(a);

View File

@ -41,6 +41,8 @@ union sockaddr_union {
uint8_t un_buffer[sizeof(struct sockaddr_un) + 1]; uint8_t un_buffer[sizeof(struct sockaddr_un) + 1];
}; };
#define SUN_PATH_LEN (sizeof(((struct sockaddr_un){}).sun_path))
typedef struct SocketAddress { typedef struct SocketAddress {
union sockaddr_union sockaddr; union sockaddr_union sockaddr;
@ -68,12 +70,6 @@ typedef enum SocketAddressBindIPv6Only {
const char* socket_address_type_to_string(int t) _const_; const char* socket_address_type_to_string(int t) _const_;
int socket_address_type_from_string(const char *s) _pure_; int socket_address_type_from_string(const char *s) _pure_;
int socket_address_parse(SocketAddress *a, const char *s);
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
int socket_address_print(const SocketAddress *a, char **p);
int socket_address_verify(const SocketAddress *a, bool strict) _pure_;
int sockaddr_un_unlink(const struct sockaddr_un *sa); int sockaddr_un_unlink(const struct sockaddr_un *sa);
static inline int socket_address_unlink(const SocketAddress *a) { static inline int socket_address_unlink(const SocketAddress *a) {
@ -94,11 +90,9 @@ int socket_address_listen(
mode_t directory_mode, mode_t directory_mode,
mode_t socket_mode, mode_t socket_mode,
const char *label); const char *label);
int make_socket_fd(int log_level, const char* address, int type, int flags);
bool socket_address_is(const SocketAddress *a, const char *s, int type);
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
int socket_address_verify(const SocketAddress *a, bool strict) _pure_;
int socket_address_print(const SocketAddress *a, char **p);
bool socket_address_matches_fd(const SocketAddress *a, int fd); bool socket_address_matches_fd(const SocketAddress *a, int fd);
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_; bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_;

View File

@ -13,6 +13,7 @@ typedef enum UnitNameFlags {
UNIT_NAME_TEMPLATE = 1 << 1, /* Allow foo@.service */ UNIT_NAME_TEMPLATE = 1 << 1, /* Allow foo@.service */
UNIT_NAME_INSTANCE = 1 << 2, /* Allow foo@bar.service */ UNIT_NAME_INSTANCE = 1 << 2, /* Allow foo@bar.service */
UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE, UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE,
_UNIT_NAME_INVALID = -1,
} UnitNameFlags; } UnitNameFlags;
bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_; bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_;

View File

@ -404,11 +404,16 @@ char* gid_to_name(gid_t gid) {
return ret; return ret;
} }
static bool gid_list_has(const gid_t *list, size_t size, gid_t val) {
for (size_t i = 0; i < size; i++)
if (list[i] == val)
return true;
return false;
}
int in_gid(gid_t gid) { int in_gid(gid_t gid) {
_cleanup_free_ gid_t *allocated = NULL; _cleanup_free_ gid_t *gids = NULL;
gid_t local[16], *p = local; int ngroups;
int ngroups = ELEMENTSOF(local);
unsigned attempt = 0;
if (getgid() == gid) if (getgid() == gid)
return 1; return 1;
@ -419,6 +424,57 @@ int in_gid(gid_t gid) {
if (!gid_is_valid(gid)) if (!gid_is_valid(gid))
return -EINVAL; return -EINVAL;
ngroups = getgroups_alloc(&gids);
if (ngroups < 0)
return ngroups;
return gid_list_has(gids, ngroups, gid);
}
int in_group(const char *name) {
int r;
gid_t gid;
r = get_group_creds(&name, &gid, 0);
if (r < 0)
return r;
return in_gid(gid);
}
int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **ret) {
size_t nresult = 0;
assert(ret);
if (size2 > INT_MAX - size1)
return -ENOBUFS;
gid_t *buf = new(gid_t, size1 + size2);
if (!buf)
return -ENOMEM;
/* Duplicates need to be skipped on merging, otherwise they'll be passed on and stored in the kernel. */
for (size_t i = 0; i < size1; i++)
if (!gid_list_has(buf, nresult, list1[i]))
buf[nresult++] = list1[i];
for (size_t i = 0; i < size2; i++)
if (!gid_list_has(buf, nresult, list2[i]))
buf[nresult++] = list2[i];
*ret = buf;
return (int)nresult;
}
int getgroups_alloc(gid_t** gids) {
gid_t *allocated;
_cleanup_free_ gid_t *p = NULL;
int ngroups = 8;
unsigned attempt = 0;
allocated = new(gid_t, ngroups);
if (!allocated)
return -ENOMEM;
p = allocated;
for (;;) { for (;;) {
ngroups = getgroups(ngroups, p); ngroups = getgroups(ngroups, p);
if (ngroups >= 0) if (ngroups >= 0)
@ -447,22 +503,8 @@ int in_gid(gid_t gid) {
p = allocated; p = allocated;
} }
for (int i = 0; i < ngroups; i++) *gids = TAKE_PTR(p);
if (p[i] == gid) return ngroups;
return true;
return false;
}
int in_group(const char *name) {
int r;
gid_t gid;
r = get_group_creds(&name, &gid, 0);
if (r < 0)
return r;
return in_gid(gid);
} }
int get_home_dir(char **_h) { int get_home_dir(char **_h) {

View File

@ -42,6 +42,9 @@ char* gid_to_name(gid_t gid);
int in_gid(gid_t gid); int in_gid(gid_t gid);
int in_group(const char *name); int in_group(const char *name);
int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **result);
int getgroups_alloc(gid_t** gids);
int get_home_dir(char **ret); int get_home_dir(char **ret);
int get_shell(char **_ret); int get_shell(char **_ret);

View File

@ -12,6 +12,7 @@
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
#include "socket.h" #include "socket.h"
#include "socket-netlink.h"
#include "socket-util.h" #include "socket-util.h"
#include "string-util.h" #include "string-util.h"
#include "unit.h" #include "unit.h"

View File

@ -1193,6 +1193,10 @@ static int setup_pam(
if (pam_code != PAM_SUCCESS) if (pam_code != PAM_SUCCESS)
goto fail; goto fail;
pam_code = pam_setcred(handle, PAM_ESTABLISH_CRED | flags);
if (pam_code != PAM_SUCCESS)
goto fail;
pam_code = pam_open_session(handle, flags); pam_code = pam_open_session(handle, flags);
if (pam_code != PAM_SUCCESS) if (pam_code != PAM_SUCCESS)
goto fail; goto fail;
@ -1277,6 +1281,10 @@ static int setup_pam(
} }
} }
pam_code = pam_setcred(handle, PAM_DELETE_CRED | flags);
if (pam_code != PAM_SUCCESS)
goto child_finish;
/* If our parent died we'll end the session */ /* If our parent died we'll end the session */
if (getppid() != parent_pid) { if (getppid() != parent_pid) {
pam_code = pam_close_session(handle, flags); pam_code = pam_close_session(handle, flags);
@ -3010,6 +3018,8 @@ static int exec_child(
size_t n_fds; size_t n_fds;
ExecDirectoryType dt; ExecDirectoryType dt;
int secure_bits; int secure_bits;
_cleanup_free_ gid_t *gids_after_pam = NULL;
int ngids_after_pam = 0;
assert(unit); assert(unit);
assert(command); assert(command);
@ -3423,6 +3433,12 @@ static int exec_child(
*exit_status = EXIT_PAM; *exit_status = EXIT_PAM;
return log_unit_error_errno(unit, r, "Failed to set up PAM session: %m"); return log_unit_error_errno(unit, r, "Failed to set up PAM session: %m");
} }
ngids_after_pam = getgroups_alloc(&gids_after_pam);
if (ngids_after_pam < 0) {
*exit_status = EXIT_MEMORY;
return log_unit_error_errno(unit, ngids_after_pam, "Failed to obtain groups after setting up PAM: %m");
}
} }
} }
@ -3502,7 +3518,22 @@ static int exec_child(
* This needs to be done after PrivateDevices=y setup as device nodes should be owned by the host's root. * This needs to be done after PrivateDevices=y setup as device nodes should be owned by the host's root.
* For non-root in a userns, devices will be owned by the user/group before the group change, and nobody. */ * For non-root in a userns, devices will be owned by the user/group before the group change, and nobody. */
if (needs_setuid) { if (needs_setuid) {
r = enforce_groups(gid, supplementary_gids, ngids); _cleanup_free_ gid_t *gids_to_enforce = NULL;
int ngids_to_enforce = 0;
ngids_to_enforce = merge_gid_lists(supplementary_gids,
ngids,
gids_after_pam,
ngids_after_pam,
&gids_to_enforce);
if (ngids_to_enforce < 0) {
*exit_status = EXIT_MEMORY;
return log_unit_error_errno(unit,
ngids_to_enforce,
"Failed to merge group lists. Group membership might be incorrect: %m");
}
r = enforce_groups(gid, gids_to_enforce, ngids_to_enforce);
if (r < 0) { if (r < 0) {
*exit_status = EXIT_GROUP; *exit_status = EXIT_GROUP;
return log_unit_error_errno(unit, r, "Changing group credentials failed: %m"); return log_unit_error_errno(unit, r, "Changing group credentials failed: %m");

View File

@ -57,7 +57,7 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) {
/* Processes with argv[0][0] = '@' we ignore from the killing spree. /* Processes with argv[0][0] = '@' we ignore from the killing spree.
* *
* http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */ * https://systemd.io/ROOT_STORAGE_DAEMONS */
if (c != '@') if (c != '@')
return false; return false;

View File

@ -11,24 +11,6 @@
#include "unit-name.h" #include "unit-name.h"
#include "unit.h" #include "unit.h"
static int unit_name_compatible(const char *a, const char *b) {
_cleanup_free_ char *template = NULL;
int r;
/* The straightforward case: the symlink name matches the target */
if (streq(a, b))
return 1;
r = unit_name_template(a, &template);
if (r == -EINVAL)
return 0; /* Not a template */
if (r < 0)
return r; /* OOM, or some other failure. Just skip the warning. */
/* An instance name points to a target that is just the template name */
return streq(template, b);
}
static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suffix) { static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suffix) {
_cleanup_strv_free_ char **paths = NULL; _cleanup_strv_free_ char **paths = NULL;
char **p; char **p;
@ -83,9 +65,10 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suff
/* We don't treat this as an error, especially because we didn't check this for a /* We don't treat this as an error, especially because we didn't check this for a
* long time. Nevertheless, we warn, because such mismatch can be mighty confusing. */ * long time. Nevertheless, we warn, because such mismatch can be mighty confusing. */
r = unit_name_compatible(entry, basename(target)); r = unit_symlink_name_compatible(entry, basename(target), u->instance);
if (r < 0) { if (r < 0) {
log_unit_warning_errno(u, r, "Can't check if names %s and %s are compatible, ignoring: %m", entry, basename(target)); log_unit_warning_errno(u, r, "Can't check if names %s and %s are compatible, ignoring: %m",
entry, basename(target));
continue; continue;
} }
if (r == 0) if (r == 0)

View File

@ -48,6 +48,7 @@
#endif #endif
#include "securebits-util.h" #include "securebits-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "socket-netlink.h"
#include "stat-util.h" #include "stat-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"

View File

@ -1063,14 +1063,6 @@ static int apply_mount(
return 0; return 0;
} }
/* Change per-mount flags on an existing mount */
static int bind_remount_one(const char *path, unsigned long orig_flags, unsigned long new_flags, unsigned long flags_mask) {
if (mount(NULL, path, NULL, (orig_flags & ~flags_mask) | MS_REMOUNT | MS_BIND | new_flags, NULL) < 0)
return -errno;
return 0;
}
static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) { static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) {
unsigned long new_flags = 0, flags_mask = 0; unsigned long new_flags = 0, flags_mask = 0;
bool submounts = false; bool submounts = false;
@ -1102,7 +1094,7 @@ static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self
if (submounts) if (submounts)
r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, blacklist, proc_self_mountinfo); r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, blacklist, proc_self_mountinfo);
else else
r = bind_remount_one(mount_entry_path(m), m->flags, new_flags, flags_mask); r = bind_remount_one_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, proc_self_mountinfo);
/* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked /* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked
* read-only already stays this way. This improves compatibility with container managers, where we * read-only already stays this way. This improves compatibility with container managers, where we

View File

@ -36,6 +36,7 @@
#include "signal-util.h" #include "signal-util.h"
#include "smack-util.h" #include "smack-util.h"
#include "socket.h" #include "socket.h"
#include "socket-netlink.h"
#include "special.h" #include "special.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"

View File

@ -17,6 +17,7 @@
#include "process-util.h" #include "process-util.h"
#include "rlimit-util.h" #include "rlimit-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "socket-netlink.h"
#include "socket-util.h" #include "socket-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "string-table.h" #include "string-table.h"

View File

@ -2428,15 +2428,6 @@ int main(int argc, char *argv[]) {
} }
r = sd_journal_previous(j); r = sd_journal_previous(j);
} else if (arg_lines >= 0) {
r = sd_journal_seek_tail(j);
if (r < 0) {
log_error_errno(r, "Failed to seek to tail: %m");
goto finish;
}
r = sd_journal_previous_skip(j, arg_lines);
} else if (arg_reverse) { } else if (arg_reverse) {
r = sd_journal_seek_tail(j); r = sd_journal_seek_tail(j);
if (r < 0) { if (r < 0) {
@ -2446,6 +2437,15 @@ int main(int argc, char *argv[]) {
r = sd_journal_previous(j); r = sd_journal_previous(j);
} else if (arg_lines >= 0) {
r = sd_journal_seek_tail(j);
if (r < 0) {
log_error_errno(r, "Failed to seek to tail: %m");
goto finish;
}
r = sd_journal_previous_skip(j, arg_lines);
} else { } else {
r = sd_journal_seek_head(j); r = sd_journal_seek_head(j);
if (r < 0) { if (r < 0) {
@ -2514,7 +2514,7 @@ int main(int argc, char *argv[]) {
goto finish; goto finish;
} }
if (usec > arg_until) if (usec > arg_until)
goto finish; break;
} }
if (arg_since_set && arg_reverse) { if (arg_since_set && arg_reverse) {
@ -2526,7 +2526,7 @@ int main(int argc, char *argv[]) {
goto finish; goto finish;
} }
if (usec < arg_since) if (usec < arg_since)
goto finish; break;
} }
if (!arg_merge && !arg_quiet) { if (!arg_merge && !arg_quiet) {
@ -2632,6 +2632,17 @@ int main(int argc, char *argv[]) {
if (!arg_follow) { if (!arg_follow) {
if (n_shown == 0 && !arg_quiet) if (n_shown == 0 && !arg_quiet)
printf("-- No entries --\n"); printf("-- No entries --\n");
break;
}
fflush(stdout);
r = wait_for_change(j, poll_fd);
if (r < 0)
goto finish;
first_line = false;
}
if (arg_show_cursor || arg_cursor_file) { if (arg_show_cursor || arg_cursor_file) {
_cleanup_free_ char *cursor = NULL; _cleanup_free_ char *cursor = NULL;
@ -2655,18 +2666,6 @@ int main(int argc, char *argv[]) {
} }
} }
break;
}
fflush(stdout);
r = wait_for_change(j, poll_fd);
if (r < 0)
goto finish;
first_line = false;
}
finish: finish:
pager_close(); pager_close();

View File

@ -340,17 +340,17 @@ int device_set_devtype(sd_device *device, const char *_devtype) {
return 0; return 0;
} }
int device_set_ifindex(sd_device *device, const char *_ifindex) { int device_set_ifindex(sd_device *device, const char *name) {
int ifindex, r; int r, ifindex;
assert(device); assert(device);
assert(_ifindex); assert(name);
r = parse_ifindex(_ifindex, &ifindex); ifindex = parse_ifindex(name);
if (r < 0) if (ifindex < 0)
return r; return ifindex;
r = device_add_property_internal(device, "IFINDEX", _ifindex); r = device_add_property_internal(device, "IFINDEX", name);
if (r < 0) if (r < 0)
return r; return r;
@ -619,7 +619,7 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
struct ifreq ifr = {}; struct ifreq ifr = {};
int ifindex; int ifindex;
r = parse_ifindex(&id[1], &ifr.ifr_ifindex); r = ifr.ifr_ifindex = parse_ifindex(&id[1]);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -928,7 +928,8 @@ _public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) {
*(char*) (mempcpy(buf, word, l)) = 0; *(char*) (mempcpy(buf, word, l)) = 0;
if (parse_ifindex(buf, &ifi) < 0) ifi = parse_ifindex(buf);
if (ifi < 0)
continue; continue;
if (!GREEDY_REALLOC(ni, allocated, nr+1)) { if (!GREEDY_REALLOC(ni, allocated, nr+1)) {

View File

@ -167,14 +167,15 @@ int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifn
return 0; return 0;
} }
int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, int *ret) { int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name) {
_cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
int r; int r, ret;
assert(rtnl);
assert(name); assert(name);
assert(ret);
if (!rtnl)
rtnl = &our_rtnl;
if (!*rtnl) { if (!*rtnl) {
r = sd_netlink_open(rtnl); r = sd_netlink_open(rtnl);
if (r < 0) if (r < 0)
@ -190,10 +191,16 @@ int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, int
return r; return r;
r = sd_netlink_call(*rtnl, message, 0, &reply); r = sd_netlink_call(*rtnl, message, 0, &reply);
if (r == -EINVAL)
return -ENODEV; /* The device doesn't exist */
if (r < 0) if (r < 0)
return r; return r;
return sd_rtnl_message_link_get_ifindex(reply, ret); r = sd_rtnl_message_link_get_ifindex(reply, &ret);
if (r < 0)
return r;
assert(ret > 0);
return ret;
} }
int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) { int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {

View File

@ -51,7 +51,7 @@ int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu); int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu);
int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names); int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names);
int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names); int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names);
int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, int *ret); int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name);
int rtnl_log_parse_error(int r); int rtnl_log_parse_error(int r);
int rtnl_log_create_error(int r); int rtnl_log_create_error(int r);

View File

@ -276,7 +276,6 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
_cleanup_free_ int *ifis = NULL; _cleanup_free_ int *ifis = NULL;
_cleanup_free_ char *s = NULL; _cleanup_free_ char *s = NULL;
size_t allocated = 0, c = 0; size_t allocated = 0, c = 0;
const char *x;
int r; int r;
assert_return(ifindex > 0, -EINVAL); assert_return(ifindex > 0, -EINVAL);
@ -289,7 +288,7 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
if (r < 0) if (r < 0)
return r; return r;
for (x = s;;) { for (const char *x = s;;) {
_cleanup_free_ char *word = NULL; _cleanup_free_ char *word = NULL;
r = extract_first_word(&x, &word, NULL, 0); r = extract_first_word(&x, &word, NULL, 0);
@ -298,14 +297,12 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
if (r == 0) if (r == 0)
break; break;
r = parse_ifindex(word, &ifindex);
if (r < 0)
return r;
if (!GREEDY_REALLOC(ifis, allocated, c + 2)) if (!GREEDY_REALLOC(ifis, allocated, c + 2))
return -ENOMEM; return -ENOMEM;
ifis[c++] = ifindex; r = ifis[c++] = parse_ifindex(word);
if (r < 0)
return r;
} }
if (ifis) if (ifis)

View File

@ -294,12 +294,11 @@ int machine_load(Machine *m) {
if (netif) { if (netif) {
size_t allocated = 0, nr = 0; size_t allocated = 0, nr = 0;
const char *p; const char *p;
int *ni = NULL; _cleanup_free_ int *ni = NULL;
p = netif; p = netif;
for (;;) { for (;;) {
_cleanup_free_ char *word = NULL; _cleanup_free_ char *word = NULL;
int ifi;
r = extract_first_word(&p, &word, NULL, 0); r = extract_first_word(&p, &word, NULL, 0);
if (r == 0) if (r == 0)
@ -311,19 +310,18 @@ int machine_load(Machine *m) {
break; break;
} }
if (parse_ifindex(word, &ifi) < 0) r = parse_ifindex(word);
if (r < 0)
continue; continue;
if (!GREEDY_REALLOC(ni, allocated, nr+1)) { if (!GREEDY_REALLOC(ni, allocated, nr + 1))
free(ni);
return log_oom(); return log_oom();
}
ni[nr++] = ifi; ni[nr++] = r;
} }
free(m->netif); free(m->netif);
m->netif = ni; m->netif = TAKE_PTR(ni);
m->n_netif = nr; m->n_netif = nr;
} }

View File

@ -591,11 +591,9 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
printf("\t Root: %s\n", i->root_directory); printf("\t Root: %s\n", i->root_directory);
if (i->n_netif > 0) { if (i->n_netif > 0) {
size_t c;
fputs("\t Iface:", stdout); fputs("\t Iface:", stdout);
for (c = 0; c < i->n_netif; c++) { for (size_t c = 0; c < i->n_netif; c++) {
char name[IF_NAMESIZE+1]; char name[IF_NAMESIZE+1];
if (format_ifname(i->netif[c], name)) { if (format_ifname(i->netif[c], name)) {
@ -737,7 +735,7 @@ static int show_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
bool properties, new_line = false; bool properties, new_line = false;
sd_bus *bus = userdata; sd_bus *bus = userdata;
int r = 0, i; int r = 0;
assert(bus); assert(bus);
@ -754,7 +752,7 @@ static int show_machine(int argc, char *argv[], void *userdata) {
return r; return r;
} }
for (i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
const char *path = NULL; const char *path = NULL;
r = sd_bus_call_method(bus, r = sd_bus_call_method(bus,
@ -1075,7 +1073,7 @@ static int show_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
bool properties, new_line = false; bool properties, new_line = false;
sd_bus *bus = userdata; sd_bus *bus = userdata;
int r = 0, i; int r = 0;
assert(bus); assert(bus);
@ -1096,7 +1094,7 @@ static int show_image(int argc, char *argv[], void *userdata) {
return r; return r;
} }
for (i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
const char *path = NULL; const char *path = NULL;
r = sd_bus_call_method( r = sd_bus_call_method(
@ -1127,7 +1125,7 @@ static int show_image(int argc, char *argv[], void *userdata) {
static int kill_machine(int argc, char *argv[], void *userdata) { static int kill_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata; sd_bus *bus = userdata;
int r, i; int r;
assert(bus); assert(bus);
@ -1136,7 +1134,7 @@ static int kill_machine(int argc, char *argv[], void *userdata) {
if (!arg_kill_who) if (!arg_kill_who)
arg_kill_who = "all"; arg_kill_who = "all";
for (i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
r = sd_bus_call_method( r = sd_bus_call_method(
bus, bus,
"org.freedesktop.machine1", "org.freedesktop.machine1",
@ -1170,13 +1168,13 @@ static int poweroff_machine(int argc, char *argv[], void *userdata) {
static int terminate_machine(int argc, char *argv[], void *userdata) { static int terminate_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata; sd_bus *bus = userdata;
int r, i; int r;
assert(bus); assert(bus);
polkit_agent_open_if_enabled(arg_transport, arg_ask_password); polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
for (i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
r = sd_bus_call_method( r = sd_bus_call_method(
bus, bus,
"org.freedesktop.machine1", "org.freedesktop.machine1",
@ -1541,13 +1539,13 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
static int remove_image(int argc, char *argv[], void *userdata) { static int remove_image(int argc, char *argv[], void *userdata) {
sd_bus *bus = userdata; sd_bus *bus = userdata;
int r, i; int r;
assert(bus); assert(bus);
polkit_agent_open_if_enabled(arg_transport, arg_ask_password); polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
for (i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
@ -1709,7 +1707,7 @@ static int start_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
sd_bus *bus = userdata; sd_bus *bus = userdata;
int r, i; int r;
assert(bus); assert(bus);
@ -1720,7 +1718,7 @@ static int start_machine(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return log_oom(); return log_oom();
for (i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *unit = NULL; _cleanup_free_ char *unit = NULL;
const char *object; const char *object;
@ -1772,7 +1770,7 @@ static int enable_machine(int argc, char *argv[], void *userdata) {
size_t n_changes = 0; size_t n_changes = 0;
const char *method = NULL; const char *method = NULL;
sd_bus *bus = userdata; sd_bus *bus = userdata;
int r, i; int r;
assert(bus); assert(bus);
@ -1794,7 +1792,7 @@ static int enable_machine(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
for (i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
_cleanup_free_ char *unit = NULL; _cleanup_free_ char *unit = NULL;
r = make_service_name(argv[i], &unit); r = make_service_name(argv[i], &unit);
@ -2435,7 +2433,7 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ TransferInfo *transfers = NULL; _cleanup_free_ TransferInfo *transfers = NULL;
size_t n_transfers = 0, n_allocated = 0, j; size_t n_transfers = 0, n_allocated = 0;
const char *type, *remote, *local; const char *type, *remote, *local;
sd_bus *bus = userdata; sd_bus *bus = userdata;
uint32_t id, max_id = 0; uint32_t id, max_id = 0;
@ -2505,7 +2503,7 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
(int) max_local, "LOCAL", (int) max_local, "LOCAL",
(int) max_remote, "REMOTE"); (int) max_remote, "REMOTE");
for (j = 0; j < n_transfers; j++) for (size_t j = 0; j < n_transfers; j++)
if (transfers[j].progress < 0) if (transfers[j].progress < 0)
printf("%*" PRIu32 " %*s %-*s %-*s %-*s\n", printf("%*" PRIu32 " %*s %-*s %-*s %-*s\n",
@ -2535,13 +2533,13 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
static int cancel_transfer(int argc, char *argv[], void *userdata) { static int cancel_transfer(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata; sd_bus *bus = userdata;
int r, i; int r;
assert(bus); assert(bus);
polkit_agent_open_if_enabled(arg_transport, arg_ask_password); polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
for (i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
uint32_t id; uint32_t id;
r = safe_atou32(argv[i], &id); r = safe_atou32(argv[i], &id);
@ -3031,7 +3029,6 @@ static int parse_argv(int argc, char *argv[]) {
done: done:
if (shell >= 0) { if (shell >= 0) {
char *t; char *t;
int i;
/* We found the "shell" verb while processing the argument list. Since we turned off reordering of the /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
* argument list initially let's readjust it now, and move the "shell" verb to the back. */ * argument list initially let's readjust it now, and move the "shell" verb to the back. */
@ -3039,7 +3036,7 @@ done:
optind -= 1; /* place the option index where the "shell" verb will be placed */ optind -= 1; /* place the option index where the "shell" verb will be placed */
t = argv[shell]; t = argv[shell];
for (i = shell; i < optind; i++) for (int i = shell; i < optind; i++)
argv[i] = argv[i+1]; argv[i] = argv[i+1];
argv[optind] = t; argv[optind] = t;
} }

View File

@ -38,6 +38,7 @@
#include "parse-util.h" #include "parse-util.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "set.h" #include "set.h"
#include "socket-netlink.h"
#include "socket-util.h" #include "socket-util.h"
#include "sort-util.h" #include "sort-util.h"
#include "sparse-endian.h" #include "sparse-endian.h"
@ -1904,12 +1905,9 @@ static int link_delete(int argc, char *argv[], void *userdata) {
return log_oom(); return log_oom();
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
r = parse_ifindex_or_ifname(argv[i], &index); index = resolve_interface_or_warn(&rtnl, argv[i]);
if (r < 0) { if (index < 0)
r = rtnl_resolve_link_alternative_name(&rtnl, argv[i], &index); return index;
if (r < 0)
return log_error_errno(r, "Failed to resolve interface %s", argv[i]);
}
r = set_put(indexes, INT_TO_PTR(index)); r = set_put(indexes, INT_TO_PTR(index));
if (r < 0) if (r < 0)
@ -1960,12 +1958,9 @@ static int link_renew(int argc, char *argv[], void *userdata) {
return log_error_errno(r, "Failed to connect system bus: %m"); return log_error_errno(r, "Failed to connect system bus: %m");
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
r = parse_ifindex_or_ifname(argv[i], &index); index = resolve_interface_or_warn(&rtnl, argv[i]);
if (r < 0) { if (index < 0)
r = rtnl_resolve_link_alternative_name(&rtnl, argv[i], &index); return index;
if (r < 0)
return log_error_errno(r, "Failed to resolve interface %s", argv[i]);
}
r = link_renew_one(bus, index, argv[i]); r = link_renew_one(bus, index, argv[i]);
if (r < 0 && k >= 0) if (r < 0 && k >= 0)
@ -2015,12 +2010,9 @@ static int verb_reconfigure(int argc, char *argv[], void *userdata) {
return log_oom(); return log_oom();
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
r = parse_ifindex_or_ifname(argv[i], &index); index = resolve_interface_or_warn(&rtnl, argv[i]);
if (r < 0) { if (index < 0)
r = rtnl_resolve_link_alternative_name(&rtnl, argv[i], &index); return index;
if (r < 0)
return log_error_errno(r, "Failed to resolve interface %s", argv[i]);
}
r = set_put(indexes, INT_TO_PTR(index)); r = set_put(indexes, INT_TO_PTR(index));
if (r < 0) if (r < 0)
@ -2039,7 +2031,8 @@ static int verb_reconfigure(int argc, char *argv[], void *userdata) {
if (r < 0) { if (r < 0) {
char ifname[IF_NAMESIZE + 1]; char ifname[IF_NAMESIZE + 1];
return log_error_errno(r, "Failed to reconfigure network interface %s: %m", format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX)); return log_error_errno(r, "Failed to reconfigure network interface %s: %m",
format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
} }
} }

View File

@ -715,8 +715,8 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
if (r <= 0) if (r <= 0)
return 0; return 0;
r = parse_ifindex(identifier, &ifindex); ifindex = parse_ifindex(identifier);
if (r < 0) if (ifindex < 0)
return 0; return 0;
r = link_get(m, ifindex, &link); r = link_get(m, ifindex, &link);

View File

@ -7,12 +7,12 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "bus-common-errors.h" #include "bus-common-errors.h"
#include "bus-util.h" #include "bus-util.h"
#include "netlink-util.h"
#include "networkd-link-bus.h" #include "networkd-link-bus.h"
#include "networkd-link.h" #include "networkd-link.h"
#include "networkd-manager-bus.h" #include "networkd-manager-bus.h"
#include "networkd-manager.h" #include "networkd-manager.h"
#include "path-util.h" #include "path-util.h"
#include "socket-netlink.h"
#include "strv.h" #include "strv.h"
#include "user-util.h" #include "user-util.h"
@ -66,12 +66,9 @@ static int method_get_link_by_name(sd_bus_message *message, void *userdata, sd_b
if (r < 0) if (r < 0)
return r; return r;
index = if_nametoindex(name); index = resolve_ifname(&manager->rtnl, name);
if (index <= 0) { if (index < 0)
r = rtnl_resolve_link_alternative_name(&manager->rtnl, name, &index); return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %s cannot be resolved", name);
if (r < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %s not known", name);
}
link = hashmap_get(manager->links, INT_TO_PTR(index)); link = hashmap_get(manager->links, INT_TO_PTR(index));
if (!link) if (!link)

View File

@ -12,6 +12,7 @@
#include "networkd-route.h" #include "networkd-route.h"
#include "parse-util.h" #include "parse-util.h"
#include "set.h" #include "set.h"
#include "socket-netlink.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "strxcpyx.h" #include "strxcpyx.h"
@ -1646,12 +1647,13 @@ int config_parse_multipath_route(
m->gateway.family = family; m->gateway.family = family;
if (dev) { if (dev) {
r = parse_ifindex_or_ifname(dev, &m->ifindex); r = resolve_interface(NULL, dev);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, log_syntax(unit, LOG_ERR, filename, line, r,
"Invalid interface name or index, ignoring assignment: %s", dev); "Invalid interface name or index, ignoring assignment: %s", dev);
return 0; return 0;
} }
m->ifindex = r;
} }
if (!isempty(p)) { if (!isempty(p)) {

View File

@ -18,6 +18,7 @@
#include "nspawn-network.h" #include "nspawn-network.h"
#include "parse-util.h" #include "parse-util.h"
#include "siphash24.h" #include "siphash24.h"
#include "socket-netlink.h"
#include "socket-util.h" #include "socket-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "string-util.h" #include "string-util.h"
@ -280,7 +281,8 @@ int setup_veth(const char *machine_name,
if (r < 0) if (r < 0)
return r; return r;
u = if_nametoindex(n); u = if_nametoindex(n); /* We don't need to use resolve_ifname() here because the
* name we assigned is always the main name. */
if (u == 0) if (u == 0)
return log_error_errno(errno, "Failed to resolve interface %s: %m", n); return log_error_errno(errno, "Failed to resolve interface %s: %m", n);
@ -337,9 +339,9 @@ static int join_bridge(sd_netlink *rtnl, const char *veth_name, const char *brid
assert(veth_name); assert(veth_name);
assert(bridge_name); assert(bridge_name);
r = parse_ifindex_or_ifname(bridge_name, &bridge_ifi); bridge_ifi = resolve_interface(&rtnl, bridge_name);
if (r < 0) if (bridge_ifi < 0)
return r; return bridge_ifi;
r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0); r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0);
if (r < 0) if (r < 0)
@ -472,16 +474,6 @@ int remove_bridge(const char *bridge_name) {
return remove_one_link(rtnl, bridge_name); return remove_one_link(rtnl, bridge_name);
} }
static int parse_interface(const char *name) {
int ifi, r;
r = parse_ifindex_or_ifname(name, &ifi);
if (r < 0)
return log_error_errno(r, "Failed to resolve interface %s: %m", name);
return ifi;
}
int test_network_interface_initialized(const char *name) { int test_network_interface_initialized(const char *name) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL; _cleanup_(sd_device_unrefp) sd_device *d = NULL;
int ifi, r; int ifi, r;
@ -492,7 +484,7 @@ int test_network_interface_initialized(const char *name) {
/* udev should be around. */ /* udev should be around. */
ifi = parse_interface(name); ifi = resolve_interface_or_warn(NULL, name);
if (ifi < 0) if (ifi < 0)
return ifi; return ifi;
@ -532,7 +524,7 @@ int move_network_interfaces(int netns_fd, char **ifaces) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int ifi; int ifi;
ifi = parse_interface(*i); ifi = resolve_interface_or_warn(&rtnl, *i);
if (ifi < 0) if (ifi < 0)
return ifi; return ifi;
@ -571,7 +563,7 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
struct ether_addr mac; struct ether_addr mac;
int ifi; int ifi;
ifi = parse_interface(*i); ifi = resolve_interface_or_warn(&rtnl, *i);
if (ifi < 0) if (ifi < 0)
return ifi; return ifi;
@ -657,7 +649,7 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
_cleanup_free_ char *n = NULL, *a = NULL; _cleanup_free_ char *n = NULL, *a = NULL;
int ifi; int ifi;
ifi = parse_interface(*i); ifi = resolve_interface_or_warn(&rtnl, *i);
if (ifi < 0) if (ifi < 0)
return ifi; return ifi;

View File

@ -16,7 +16,6 @@
#include "escape.h" #include "escape.h"
#include "format-util.h" #include "format-util.h"
#include "gcrypt-util.h" #include "gcrypt-util.h"
#include "in-addr-util.h"
#include "main-func.h" #include "main-func.h"
#include "missing_network.h" #include "missing_network.h"
#include "netlink-util.h" #include "netlink-util.h"
@ -27,6 +26,8 @@
#include "resolvectl.h" #include "resolvectl.h"
#include "resolved-def.h" #include "resolved-def.h"
#include "resolved-dns-packet.h" #include "resolved-dns-packet.h"
#include "socket-netlink.h"
#include "stdio-util.h"
#include "string-table.h" #include "string-table.h"
#include "strv.h" #include "strv.h"
#include "terminal-util.h" #include "terminal-util.h"
@ -80,7 +81,7 @@ typedef enum StatusMode {
int ifname_mangle(const char *s) { int ifname_mangle(const char *s) {
_cleanup_free_ char *iface = NULL; _cleanup_free_ char *iface = NULL;
const char *dot; const char *dot;
int ifi, r; int ifi;
assert(s); assert(s);
@ -94,14 +95,14 @@ int ifname_mangle(const char *s) {
if (!iface) if (!iface)
return log_oom(); return log_oom();
r = parse_ifindex_or_ifname(iface, &ifi); ifi = resolve_interface(NULL, iface);
if (r < 0) { if (ifi < 0) {
if (r == -ENODEV && arg_ifindex_permissive) { if (ifi == -ENODEV && arg_ifindex_permissive) {
log_debug("Interface '%s' not found, but -f specified, ignoring.", iface); log_debug("Interface '%s' not found, but -f specified, ignoring.", iface);
return 0; /* done */ return 0; /* done */
} }
return log_error_errno(r, "Unknown interface '%s': %m", iface); return log_error_errno(ifi, "Failed to resolve interface \"%s\": %m", iface);
} }
if (arg_ifindex > 0 && arg_ifindex != ifi) if (arg_ifindex > 0 && arg_ifindex != ifi)
@ -1382,8 +1383,8 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(link_info_clear) struct link_info link_info = {}; _cleanup_(link_info_clear) struct link_info link_info = {};
_cleanup_free_ char *ifi = NULL, *p = NULL; _cleanup_free_ char *p = NULL;
char ifname[IF_NAMESIZE + 1] = ""; char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = "";
char **i; char **i;
int r; int r;
@ -1397,9 +1398,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
name = ifname; name = ifname;
} }
if (asprintf(&ifi, "%i", ifindex) < 0) xsprintf(ifi, "%i", ifindex);
return log_oom();
r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifi, &p); r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifi, &p);
if (r < 0) if (r < 0)
return log_oom(); return log_oom();
@ -1819,18 +1818,19 @@ static int status_all(sd_bus *bus, StatusMode mode) {
static int verb_status(int argc, char **argv, void *userdata) { static int verb_status(int argc, char **argv, void *userdata) {
sd_bus *bus = userdata; sd_bus *bus = userdata;
int q, r = 0; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r = 0;
if (argc > 1) { if (argc > 1) {
char **ifname; char **ifname;
bool empty_line = false; bool empty_line = false;
STRV_FOREACH(ifname, argv + 1) { STRV_FOREACH(ifname, argv + 1) {
int ifindex; int ifindex, q;
q = parse_ifindex_or_ifname(*ifname, &ifindex); ifindex = resolve_interface(&rtnl, *ifname);
if (q < 0) { if (ifindex < 0) {
log_error_errno(q, "Unknown interface '%s', ignoring: %m", *ifname); log_warning_errno(ifindex, "Failed to resolve interface \"%s\", ignoring: %m", *ifname);
continue; continue;
} }

View File

@ -12,6 +12,7 @@
#include "resolved-dnssd-bus.h" #include "resolved-dnssd-bus.h"
#include "resolved-dnssd.h" #include "resolved-dnssd.h"
#include "resolved-link-bus.h" #include "resolved-link-bus.h"
#include "socket-netlink.h"
#include "stdio-util.h" #include "stdio-util.h"
#include "strv.h" #include "strv.h"
#include "user-util.h" #include "user-util.h"

View File

@ -9,6 +9,7 @@
#include "hostname-util.h" #include "hostname-util.h"
#include "resolved-dns-synthesize.h" #include "resolved-dns-synthesize.h"
#include "resolved-etc-hosts.h" #include "resolved-etc-hosts.h"
#include "socket-netlink.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "time-util.h" #include "time-util.h"

View File

@ -712,9 +712,8 @@ const sd_bus_vtable link_vtable[] = {
int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
_cleanup_free_ char *e = NULL; _cleanup_free_ char *e = NULL;
Manager *m = userdata; Manager *m = userdata;
int ifindex;
Link *link; Link *link;
int r; int ifindex, r;
assert(bus); assert(bus);
assert(path); assert(path);
@ -726,8 +725,8 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
if (r <= 0) if (r <= 0)
return 0; return 0;
r = parse_ifindex(e, &ifindex); ifindex = parse_ifindex(e);
if (r < 0) if (ifindex < 0)
return 0; return 0;
link = hashmap_get(m->links, INT_TO_PTR(ifindex)); link = hashmap_get(m->links, INT_TO_PTR(ifindex));

View File

@ -1466,7 +1466,6 @@ void manager_reset_server_features(Manager *m) {
void manager_cleanup_saved_user(Manager *m) { void manager_cleanup_saved_user(Manager *m) {
_cleanup_closedir_ DIR *d = NULL; _cleanup_closedir_ DIR *d = NULL;
struct dirent *de; struct dirent *de;
int r;
assert(m); assert(m);
@ -1494,8 +1493,8 @@ void manager_cleanup_saved_user(Manager *m) {
if (dot_or_dot_dot(de->d_name)) if (dot_or_dot_dot(de->d_name))
continue; continue;
r = parse_ifindex(de->d_name, &ifindex); ifindex = parse_ifindex(de->d_name);
if (r < 0) /* Probably some temporary file from a previous run. Delete it */ if (ifindex < 0) /* Probably some temporary file from a previous run. Delete it */
goto rm; goto rm;
l = hashmap_get(m->links, INT_TO_PTR(ifindex)); l = hashmap_get(m->links, INT_TO_PTR(ifindex));

View File

@ -1,9 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */ /* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h" #include "alloc-util.h"
#include "in-addr-util.h"
#include "macro.h" #include "macro.h"
#include "resolved-util.h" #include "resolved-util.h"
#include "socket-netlink.h"
int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) { int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
_cleanup_free_ char *buf = NULL, *name = NULL; _cleanup_free_ char *buf = NULL, *name = NULL;

View File

@ -1127,7 +1127,7 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigne
if (r < 0) if (r < 0)
return r; return r;
return strv_free_and_replace(*p, l); return strv_extend_strv(p, l, false);
} }
case SD_BUS_TYPE_BOOLEAN: { case SD_BUS_TYPE_BOOLEAN: {

View File

@ -1703,6 +1703,82 @@ static int install_info_discover_and_check(
return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes); return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes);
} }
int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char **ret_dst) {
_cleanup_free_ char *dst_updated = NULL;
int r;
/* Verify that dst is a valid either a valid alias or a valid .wants/.requires symlink for the target
* unit *i. Return negative on error or if not compatible, zero on success.
*
* ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is
* inserted into dst. It is not normally set, even on success, so that the caller can easily
* distinguish the case where instance propagation occured.
*/
const char *path_alias = strrchr(dst, '/');
if (path_alias) {
/* This branch covers legacy Alias= function of creating .wants and .requires symlinks. */
_cleanup_free_ char *dir = NULL;
char *p;
path_alias ++; /* skip over slash */
dir = dirname_malloc(dst);
if (!dir)
return log_oom();
p = endswith(dir, ".wants");
if (!p)
p = endswith(dir, ".requires");
if (!p)
return log_warning_errno(SYNTHETIC_ERRNO(EXDEV),
"Invalid path \"%s\" in alias.", dir);
*p = '\0'; /* dir should now be a unit name */
r = unit_name_classify(dir);
if (r < 0)
return log_warning_errno(SYNTHETIC_ERRNO(EXDEV),
"Invalid unit name component \"%s\" in alias.", dir);
const bool instance_propagation = r == UNIT_NAME_TEMPLATE;
/* That's the name we want to use for verification. */
r = unit_symlink_name_compatible(path_alias, i->name, instance_propagation);
if (r < 0)
return log_error_errno(r, "Failed to verify alias validity: %m");
if (r == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EXDEV),
"Invalid unit %s symlink %s.",
i->name, dst);
} else {
/* If the symlink target has an instance set and the symlink source doesn't, we "propagate
* the instance", i.e. instantiate the symlink source with the target instance. */
if (unit_name_is_valid(dst, UNIT_NAME_TEMPLATE)) {
_cleanup_free_ char *inst = NULL;
r = unit_name_to_instance(i->name, &inst);
if (r < 0)
return log_error_errno(r, "Failed to extract instance name from %s: %m", i->name);
if (r == UNIT_NAME_INSTANCE) {
r = unit_name_replace_instance(dst, inst, &dst_updated);
if (r < 0)
return log_error_errno(r, "Failed to build unit name from %s+%s: %m",
dst, inst);
}
}
r = unit_validate_alias_symlink_and_warn(dst_updated ?: dst, i->name);
if (r < 0)
return r;
}
*ret_dst = TAKE_PTR(dst_updated);
return 0;
}
static int install_info_symlink_alias( static int install_info_symlink_alias(
UnitFileInstallInfo *i, UnitFileInstallInfo *i,
const LookupPaths *paths, const LookupPaths *paths,
@ -1719,13 +1795,17 @@ static int install_info_symlink_alias(
assert(config_path); assert(config_path);
STRV_FOREACH(s, i->aliases) { STRV_FOREACH(s, i->aliases) {
_cleanup_free_ char *alias_path = NULL, *dst = NULL; _cleanup_free_ char *alias_path = NULL, *dst = NULL, *dst_updated = NULL;
q = install_full_printf(i, *s, &dst); q = install_full_printf(i, *s, &dst);
if (q < 0) if (q < 0)
return q; return q;
alias_path = path_make_absolute(dst, config_path); q = unit_file_verify_alias(i, dst, &dst_updated);
if (q < 0)
continue;
alias_path = path_make_absolute(dst_updated ?: dst, config_path);
if (!alias_path) if (!alias_path)
return -ENOMEM; return -ENOMEM;

View File

@ -187,6 +187,8 @@ int unit_file_changes_add(UnitFileChange **changes, size_t *n_changes, UnitFileC
void unit_file_changes_free(UnitFileChange *changes, size_t n_changes); void unit_file_changes_free(UnitFileChange *changes, size_t n_changes);
void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet); void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet);
int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char **ret_dst);
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name); int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name);
const char *unit_file_state_to_string(UnitFileState s) _const_; const char *unit_file_state_to_string(UnitFileState s) _const_;

View File

@ -165,6 +165,8 @@ shared_sources = files('''
serialize.h serialize.h
sleep-config.c sleep-config.c
sleep-config.h sleep-config.h
socket-netlink.c
socket-netlink.h
spawn-ask-password-agent.c spawn-ask-password-agent.c
spawn-ask-password-agent.h spawn-ask-password-agent.h
spawn-polkit-agent.c spawn-polkit-agent.c

View File

@ -76,35 +76,57 @@ int umount_recursive(const char *prefix, int flags) {
return n; return n;
} }
/* Get the mount flags for the mountpoint at "path" from "table" */ static int get_mount_flags(
static int get_mount_flags(const char *path, unsigned long *flags, struct libmnt_table *table) { struct libmnt_table *table,
struct statvfs buf = {}; const char *path,
struct libmnt_fs *fs = NULL; unsigned long *ret) {
const char *opts = NULL; struct libmnt_fs *fs;
struct statvfs buf;
const char *opts;
int r = 0; int r = 0;
/* Get the mount flags for the mountpoint at "path" from "table". We have a fallback using statvfs()
* in place (which provides us with mostly the same info), but it's just a fallback, since using it
* means triggering autofs or NFS mounts, which we'd rather avoid needlessly. */
fs = mnt_table_find_target(table, path, MNT_ITER_FORWARD); fs = mnt_table_find_target(table, path, MNT_ITER_FORWARD);
if (!fs) { if (!fs) {
log_warning("Could not find '%s' in mount table", path); log_debug("Could not find '%s' in mount table, ignoring.", path);
goto fallback; goto fallback;
} }
opts = mnt_fs_get_vfs_options(fs); opts = mnt_fs_get_vfs_options(fs);
r = mnt_optstr_get_flags(opts, flags, mnt_get_builtin_optmap(MNT_LINUX_MAP)); if (!opts) {
*ret = 0;
return 0;
}
r = mnt_optstr_get_flags(opts, ret, mnt_get_builtin_optmap(MNT_LINUX_MAP));
if (r != 0) { if (r != 0) {
log_warning_errno(r, "Could not get flags for '%s': %m", path); log_debug_errno(r, "Could not get flags for '%s', ignoring: %m", path);
goto fallback; goto fallback;
} }
/* relatime is default and trying to set it in an unprivileged container causes EPERM */ /* MS_RELATIME is default and trying to set it in an unprivileged container causes EPERM */
*flags &= ~MS_RELATIME; *ret &= ~MS_RELATIME;
return 0; return 0;
fallback: fallback:
if (statvfs(path, &buf) < 0) if (statvfs(path, &buf) < 0)
return -errno; return -errno;
*flags = buf.f_flag; /* The statvfs() flags and the mount flags mostly have the same values, but for some cases do
* not. Hence map the flags manually. (Strictly speaking, ST_RELATIME/MS_RELATIME is the most
* prominent one that doesn't match, but that's the one we mask away anyway, see above.) */
*ret =
FLAGS_SET(buf.f_flag, ST_RDONLY) * MS_RDONLY |
FLAGS_SET(buf.f_flag, ST_NODEV) * MS_NODEV |
FLAGS_SET(buf.f_flag, ST_NOEXEC) * MS_NOEXEC |
FLAGS_SET(buf.f_flag, ST_NOSUID) * MS_NOSUID |
FLAGS_SET(buf.f_flag, ST_NOATIME) * MS_NOATIME |
FLAGS_SET(buf.f_flag, ST_NODIRATIME) * MS_NODIRATIME;
return 0; return 0;
} }
@ -118,9 +140,10 @@ int bind_remount_recursive_with_mountinfo(
FILE *proc_self_mountinfo) { FILE *proc_self_mountinfo) {
_cleanup_set_free_free_ Set *done = NULL; _cleanup_set_free_free_ Set *done = NULL;
_cleanup_free_ char *cleaned = NULL; _cleanup_free_ char *simplified = NULL;
int r; int r;
assert(prefix);
assert(proc_self_mountinfo); assert(proc_self_mountinfo);
/* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
@ -134,11 +157,11 @@ int bind_remount_recursive_with_mountinfo(
* If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
* remount operation. Note that we'll ignore the blacklist for the top-level path. */ * remount operation. Note that we'll ignore the blacklist for the top-level path. */
cleaned = strdup(prefix); simplified = strdup(prefix);
if (!cleaned) if (!simplified)
return -ENOMEM; return -ENOMEM;
path_simplify(cleaned, false); path_simplify(simplified, false);
done = set_new(&path_hash_ops); done = set_new(&path_hash_ops);
if (!done) if (!done)
@ -177,26 +200,26 @@ int bind_remount_recursive_with_mountinfo(
if (!path || !type) if (!path || !type)
continue; continue;
if (!path_startswith(path, cleaned)) if (!path_startswith(path, simplified))
continue; continue;
/* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount
* we shall operate on. */ * we shall operate on. */
if (!path_equal(path, cleaned)) { if (!path_equal(path, simplified)) {
bool blacklisted = false; bool blacklisted = false;
char **i; char **i;
STRV_FOREACH(i, blacklist) { STRV_FOREACH(i, blacklist) {
if (path_equal(*i, cleaned)) if (path_equal(*i, simplified))
continue; continue;
if (!path_startswith(*i, cleaned)) if (!path_startswith(*i, simplified))
continue; continue;
if (path_startswith(path, *i)) { if (path_startswith(path, *i)) {
blacklisted = true; blacklisted = true;
log_debug("Not remounting %s blacklisted by %s, called for %s", log_debug("Not remounting %s blacklisted by %s, called for %s",
path, *i, cleaned); path, *i, simplified);
break; break;
} }
} }
@ -211,7 +234,7 @@ int bind_remount_recursive_with_mountinfo(
* already triggered, then we will find * already triggered, then we will find
* another entry for this. */ * another entry for this. */
if (streq(type, "autofs")) { if (streq(type, "autofs")) {
top_autofs = top_autofs || path_equal(path, cleaned); top_autofs = top_autofs || path_equal(path, simplified);
continue; continue;
} }
@ -226,25 +249,24 @@ int bind_remount_recursive_with_mountinfo(
* the root is either already done, or an autofs, we * the root is either already done, or an autofs, we
* are done */ * are done */
if (set_isempty(todo) && if (set_isempty(todo) &&
(top_autofs || set_contains(done, cleaned))) (top_autofs || set_contains(done, simplified)))
return 0; return 0;
if (!set_contains(done, cleaned) && if (!set_contains(done, simplified) &&
!set_contains(todo, cleaned)) { !set_contains(todo, simplified)) {
/* The prefix directory itself is not yet a mount, make it one. */ /* The prefix directory itself is not yet a mount, make it one. */
if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0) if (mount(simplified, simplified, NULL, MS_BIND|MS_REC, NULL) < 0)
return -errno; return -errno;
orig_flags = 0; orig_flags = 0;
(void) get_mount_flags(cleaned, &orig_flags, table); (void) get_mount_flags(table, simplified, &orig_flags);
orig_flags &= ~MS_RDONLY;
if (mount(NULL, cleaned, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0) if (mount(NULL, simplified, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0)
return -errno; return -errno;
log_debug("Made top-level directory %s a mount point.", prefix); log_debug("Made top-level directory %s a mount point.", prefix);
r = set_put_strdup(done, cleaned); r = set_put_strdup(done, simplified);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -278,8 +300,7 @@ int bind_remount_recursive_with_mountinfo(
/* Try to reuse the original flag set */ /* Try to reuse the original flag set */
orig_flags = 0; orig_flags = 0;
(void) get_mount_flags(x, &orig_flags, table); (void) get_mount_flags(table, x, &orig_flags);
orig_flags &= ~MS_RDONLY;
if (mount(NULL, x, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0) if (mount(NULL, x, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0)
return -errno; return -errno;
@ -289,7 +310,12 @@ int bind_remount_recursive_with_mountinfo(
} }
} }
int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist) { int bind_remount_recursive(
const char *prefix,
unsigned long new_flags,
unsigned long flags_mask,
char **blacklist) {
_cleanup_fclose_ FILE *proc_self_mountinfo = NULL; _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
int r; int r;
@ -300,6 +326,38 @@ int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned
return bind_remount_recursive_with_mountinfo(prefix, new_flags, flags_mask, blacklist, proc_self_mountinfo); return bind_remount_recursive_with_mountinfo(prefix, new_flags, flags_mask, blacklist, proc_self_mountinfo);
} }
int bind_remount_one_with_mountinfo(
const char *path,
unsigned long new_flags,
unsigned long flags_mask,
FILE *proc_self_mountinfo) {
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
unsigned long orig_flags = 0;
int r;
assert(path);
assert(proc_self_mountinfo);
rewind(proc_self_mountinfo);
table = mnt_new_table();
if (!table)
return -ENOMEM;
r = mnt_table_parse_stream(table, proc_self_mountinfo, "/proc/self/mountinfo");
if (r < 0)
return r;
/* Try to reuse the original flag set */
(void) get_mount_flags(table, path, &orig_flags);
if (mount(NULL, path, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0)
return -errno;
return 0;
}
int mount_move_root(const char *path) { int mount_move_root(const char *path) {
assert(path); assert(path);

View File

@ -10,6 +10,7 @@ int repeat_unmount(const char *path, int flags);
int umount_recursive(const char *target, int flags); int umount_recursive(const char *target, int flags);
int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist); int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist);
int bind_remount_recursive_with_mountinfo(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist, FILE *proc_self_mountinfo); int bind_remount_recursive_with_mountinfo(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist, FILE *proc_self_mountinfo);
int bind_remount_one_with_mountinfo(const char *path, unsigned long new_flags, unsigned long flags_mask, FILE *proc_self_mountinfo);
int mount_move_root(const char *path); int mount_move_root(const char *path);

363
src/shared/socket-netlink.c Normal file
View File

@ -0,0 +1,363 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <arpa/inet.h>
#include <errno.h>
#include <net/if.h>
#include <string.h>
#include "alloc-util.h"
#include "errno-util.h"
#include "extract-word.h"
#include "log.h"
#include "memory-util.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "socket-netlink.h"
#include "socket-util.h"
#include "string-util.h"
int resolve_ifname(sd_netlink **rtnl, const char *name) {
int r;
/* Like if_nametoindex, but resolves "alternative names" too. */
assert(name);
r = if_nametoindex(name);
if (r > 0)
return r;
return rtnl_resolve_link_alternative_name(rtnl, name);
}
int resolve_interface(sd_netlink **rtnl, const char *name) {
int r;
/* Like resolve_ifname, but resolves interface numbers too. */
assert(name);
r = parse_ifindex(name);
if (r > 0)
return r;
assert(r < 0);
return resolve_ifname(rtnl, name);
}
int resolve_interface_or_warn(sd_netlink **rtnl, const char *name) {
int r;
r = resolve_interface(rtnl, name);
if (r < 0)
return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name);
return r;
}
int socket_address_parse(SocketAddress *a, const char *s) {
_cleanup_free_ char *n = NULL;
char *e;
int r;
assert(a);
assert(s);
*a = (SocketAddress) {
.type = SOCK_STREAM,
};
if (*s == '[') {
uint16_t port;
/* IPv6 in [x:.....:z]:p notation */
e = strchr(s+1, ']');
if (!e)
return -EINVAL;
n = strndup(s+1, e-s-1);
if (!n)
return -ENOMEM;
errno = 0;
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
return errno_or_else(EINVAL);
e++;
if (*e != ':')
return -EINVAL;
e++;
r = parse_ip_port(e, &port);
if (r < 0)
return r;
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htobe16(port);
a->size = sizeof(struct sockaddr_in6);
} else if (*s == '/') {
/* AF_UNIX socket */
size_t l;
l = strlen(s);
if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when
* parsing (the kernel itself is less strict here in what it
* accepts) */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
memcpy(a->sockaddr.un.sun_path, s, l);
a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
} else if (*s == '@') {
/* Abstract AF_UNIX socket */
size_t l;
l = strlen(s+1);
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated sockets here
* when parsing, even though abstract namespace sockets
* explicitly allow embedded NUL bytes and don't consider
* them special. But it's simply annoying to debug such
* sockets. */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
} else if (startswith(s, "vsock:")) {
/* AF_VSOCK socket in vsock:cid:port notation */
const char *cid_start = s + STRLEN("vsock:");
unsigned port;
e = strchr(cid_start, ':');
if (!e)
return -EINVAL;
r = safe_atou(e+1, &port);
if (r < 0)
return r;
n = strndup(cid_start, e - cid_start);
if (!n)
return -ENOMEM;
if (!isempty(n)) {
r = safe_atou(n, &a->sockaddr.vm.svm_cid);
if (r < 0)
return r;
} else
a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
a->sockaddr.vm.svm_family = AF_VSOCK;
a->sockaddr.vm.svm_port = port;
a->size = sizeof(struct sockaddr_vm);
} else {
uint16_t port;
e = strchr(s, ':');
if (e) {
r = parse_ip_port(e + 1, &port);
if (r < 0)
return r;
n = strndup(s, e-s);
if (!n)
return -ENOMEM;
/* IPv4 in w.x.y.z:p notation? */
r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
if (r < 0)
return -errno;
if (r > 0) {
/* Gotcha, it's a traditional IPv4 address */
a->sockaddr.in.sin_family = AF_INET;
a->sockaddr.in.sin_port = htobe16(port);
a->size = sizeof(struct sockaddr_in);
} else {
int idx;
/* Uh, our last resort, an interface name */
idx = resolve_ifname(NULL, n);
if (idx < 0)
return idx;
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_scope_id = idx;
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
}
} else {
/* Just a port */
r = parse_ip_port(s, &port);
if (r < 0)
return r;
if (socket_ipv6_is_supported()) {
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
} else {
a->sockaddr.in.sin_family = AF_INET;
a->sockaddr.in.sin_port = htobe16(port);
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
a->size = sizeof(struct sockaddr_in);
}
}
}
return 0;
}
int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
SocketAddress b;
int r;
/* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
r = socket_address_parse(&b, s);
if (r < 0)
return r;
if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
return -EAFNOSUPPORT;
}
*a = b;
return 0;
}
int socket_address_parse_netlink(SocketAddress *a, const char *s) {
_cleanup_free_ char *word = NULL;
unsigned group = 0;
int family, r;
assert(a);
assert(s);
zero(*a);
a->type = SOCK_RAW;
r = extract_first_word(&s, &word, NULL, 0);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
family = netlink_family_from_string(word);
if (family < 0)
return -EINVAL;
if (!isempty(s)) {
r = safe_atou(s, &group);
if (r < 0)
return r;
}
a->sockaddr.nl.nl_family = AF_NETLINK;
a->sockaddr.nl.nl_groups = group;
a->type = SOCK_RAW;
a->size = sizeof(struct sockaddr_nl);
a->protocol = family;
return 0;
}
bool socket_address_is(const SocketAddress *a, const char *s, int type) {
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse(&b, s) < 0)
return false;
b.type = type;
return socket_address_equal(a, &b);
}
bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse_netlink(&b, s) < 0)
return false;
return socket_address_equal(a, &b);
}
int make_socket_fd(int log_level, const char* address, int type, int flags) {
SocketAddress a;
int fd, r;
r = socket_address_parse(&a, address);
if (r < 0)
return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
a.type = type;
fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
NULL, false, false, false, 0755, 0644, NULL);
if (fd < 0 || log_get_max_level() >= log_level) {
_cleanup_free_ char *p = NULL;
r = socket_address_print(&a, &p);
if (r < 0)
return log_error_errno(r, "socket_address_print(): %m");
if (fd < 0)
log_error_errno(fd, "Failed to listen on %s: %m", p);
else
log_full(log_level, "Listening on %s", p);
}
return fd;
}
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret_addr, int *ret_ifindex) {
_cleanup_free_ char *buf = NULL;
const char *suffix;
int r, ifindex = 0;
assert(s);
assert(family);
assert(ret_addr);
/* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
* if one is found. */
suffix = strchr(s, '%');
if (suffix) {
if (ret_ifindex) {
/* If we shall return the interface index, try to parse it */
ifindex = resolve_interface(NULL, suffix + 1);
if (ifindex < 0)
return ifindex;
}
s = buf = strndup(s, suffix - s);
if (!buf)
return -ENOMEM;
}
r = in_addr_from_string_auto(s, family, ret_addr);
if (r < 0)
return r;
if (ret_ifindex)
*ret_ifindex = ifindex;
return r;
}

View File

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "sd-netlink.h"
#include "in-addr-util.h"
#include "macro.h"
#include "socket-util.h"
int resolve_ifname(sd_netlink **rtnl, const char *name);
int resolve_interface(sd_netlink **rtnl, const char *name);
int resolve_interface_or_warn(sd_netlink **rtnl, const char *name);
int make_socket_fd(int log_level, const char* address, int type, int flags);
int socket_address_parse(SocketAddress *a, const char *s);
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
bool socket_address_is(const SocketAddress *a, const char *s, int type);
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);

View File

@ -31,6 +31,41 @@ bool unit_type_may_template(UnitType type) {
UNIT_PATH); UNIT_PATH);
} }
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation) {
_cleanup_free_ char *template = NULL;
int r, un_type1, un_type2;
un_type1 = unit_name_classify(symlink);
/* The straightforward case: the symlink name matches the target and we have a valid unit */
if (streq(symlink, target) &&
(un_type1 & (UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE)))
return 1;
r = unit_name_template(symlink, &template);
if (r == -EINVAL)
return 0; /* Not a template */
if (r < 0)
return r;
un_type2 = unit_name_classify(target);
/* An instance name points to a target that is just the template name */
if (un_type1 == UNIT_NAME_INSTANCE &&
un_type2 == UNIT_NAME_TEMPLATE &&
streq(template, target))
return 1;
/* foo@.target.requires/bar@.service: instance will be propagated */
if (instance_propagation &&
un_type1 == UNIT_NAME_TEMPLATE &&
un_type2 == UNIT_NAME_TEMPLATE &&
streq(template, target))
return 1;
return 0;
}
int unit_validate_alias_symlink_and_warn(const char *filename, const char *target) { int unit_validate_alias_symlink_and_warn(const char *filename, const char *target) {
const char *src, *dst; const char *src, *dst;
_cleanup_free_ char *src_instance = NULL, *dst_instance = NULL; _cleanup_free_ char *src_instance = NULL, *dst_instance = NULL;

View File

@ -39,6 +39,7 @@ enum UnitFileScope {
bool unit_type_may_alias(UnitType type) _const_; bool unit_type_may_alias(UnitType type) _const_;
bool unit_type_may_template(UnitType type) _const_; bool unit_type_may_template(UnitType type) _const_;
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation);
int unit_validate_alias_symlink_and_warn(const char *filename, const char *target); int unit_validate_alias_symlink_and_warn(const char *filename, const char *target);
int unit_file_build_name_map( int unit_file_build_name_map(

View File

@ -117,6 +117,7 @@ static bool arg_dry_run = false;
static bool arg_quiet = false; static bool arg_quiet = false;
static bool arg_full = false; static bool arg_full = false;
static bool arg_recursive = false; static bool arg_recursive = false;
static bool arg_with_dependencies = false;
static bool arg_show_transaction = false; static bool arg_show_transaction = false;
static int arg_force = 0; static int arg_force = 0;
static bool arg_ask_password = false; static bool arg_ask_password = false;
@ -799,6 +800,107 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r
return 0; return 0;
} }
static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***ret) {
_cleanup_strv_free_ char **deps = NULL;
static const struct bus_properties_map map[_DEPENDENCY_MAX][6] = {
[DEPENDENCY_FORWARD] = {
{ "Requires", "as", NULL, 0 },
{ "Requisite", "as", NULL, 0 },
{ "Wants", "as", NULL, 0 },
{ "ConsistsOf", "as", NULL, 0 },
{ "BindsTo", "as", NULL, 0 },
{}
},
[DEPENDENCY_REVERSE] = {
{ "RequiredBy", "as", NULL, 0 },
{ "RequisiteOf", "as", NULL, 0 },
{ "WantedBy", "as", NULL, 0 },
{ "PartOf", "as", NULL, 0 },
{ "BoundBy", "as", NULL, 0 },
{}
},
[DEPENDENCY_AFTER] = {
{ "After", "as", NULL, 0 },
{}
},
[DEPENDENCY_BEFORE] = {
{ "Before", "as", NULL, 0 },
{}
},
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *dbus_path = NULL;
int r;
assert(bus);
assert(name);
assert(ret);
dbus_path = unit_dbus_path_from_name(name);
if (!dbus_path)
return log_oom();
r = bus_map_all_properties(bus,
"org.freedesktop.systemd1",
dbus_path,
map[arg_dependency],
BUS_MAP_STRDUP,
&error,
NULL,
&deps);
if (r < 0)
return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r));
*ret = TAKE_PTR(deps);
return 0;
}
static int append_unit_dependencies(sd_bus *bus, char **names, char ***ret) {
_cleanup_strv_free_ char **with_deps = NULL;
char **name;
assert(bus);
assert(ret);
STRV_FOREACH(name, names) {
_cleanup_strv_free_ char **deps = NULL;
if (strv_extend(&with_deps, *name) < 0)
return log_oom();
(void) list_dependencies_get_dependencies(bus, *name, &deps);
if (strv_extend_strv(&with_deps, deps, true) < 0)
return log_oom();
}
*ret = TAKE_PTR(with_deps);
return 0;
}
static int maybe_extend_with_unit_dependencies(sd_bus *bus, char ***list) {
assert(bus);
assert(list);
if (arg_with_dependencies) {
int r;
_cleanup_strv_free_ char **list_with_deps = NULL;
r = append_unit_dependencies(bus, *list, &list_with_deps);
if (r < 0)
return log_error_errno(r, "Failed to append unit dependencies: %m");
strv_free(*list);
*list = TAKE_PTR(list_with_deps);
}
return 0;
}
static int list_units(int argc, char *argv[], void *userdata) { static int list_units(int argc, char *argv[], void *userdata) {
_cleanup_free_ UnitInfo *unit_infos = NULL; _cleanup_free_ UnitInfo *unit_infos = NULL;
_cleanup_(message_set_freep) Set *replies = NULL; _cleanup_(message_set_freep) Set *replies = NULL;
@ -812,9 +914,21 @@ static int list_units(int argc, char *argv[], void *userdata) {
(void) pager_open(arg_pager_flags); (void) pager_open(arg_pager_flags);
if (arg_with_dependencies) {
_cleanup_strv_free_ char **names = NULL;
r = append_unit_dependencies(bus, strv_skip(argv, 1), &names);
if (r < 0)
return r;
r = get_unit_list_recursive(bus, names, &unit_infos, &replies, &machines);
if (r < 0)
return r;
} else {
r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
if (r < 0) if (r < 0)
return r; return r;
}
typesafe_qsort(unit_infos, r, compare_unit_info); typesafe_qsort(unit_infos, r, compare_unit_info);
return output_units_list(unit_infos, r); return output_units_list(unit_infos, r);
@ -1588,9 +1702,21 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
if (arg_with_dependencies) {
_cleanup_strv_free_ char **names_with_deps = NULL;
r = append_unit_dependencies(bus, strv_skip(argv, 1), &names_with_deps);
if (r < 0)
return log_error_errno(r, "Failed to append unit dependencies: %m");
r = sd_bus_message_append_strv(m, names_with_deps);
if (r < 0)
return bus_log_create_error(r);
} else {
r = sd_bus_message_append_strv(m, strv_skip(argv, 1)); r = sd_bus_message_append_strv(m, strv_skip(argv, 1));
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
}
r = sd_bus_call(bus, m, 0, &error, &reply); r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
@ -1694,79 +1820,6 @@ static int list_dependencies_print(const char *name, int level, unsigned branche
return 0; return 0;
} }
static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
struct DependencyStatusInfo {
char **dep[5];
} info = {};
static const struct bus_properties_map map[_DEPENDENCY_MAX][6] = {
[DEPENDENCY_FORWARD] = {
{ "Requires", "as", NULL, offsetof(struct DependencyStatusInfo, dep[0]) },
{ "Requisite", "as", NULL, offsetof(struct DependencyStatusInfo, dep[1]) },
{ "Wants", "as", NULL, offsetof(struct DependencyStatusInfo, dep[2]) },
{ "ConsistsOf", "as", NULL, offsetof(struct DependencyStatusInfo, dep[3]) },
{ "BindsTo", "as", NULL, offsetof(struct DependencyStatusInfo, dep[4]) },
{}
},
[DEPENDENCY_REVERSE] = {
{ "RequiredBy", "as", NULL, offsetof(struct DependencyStatusInfo, dep[0]) },
{ "RequisiteOf", "as", NULL, offsetof(struct DependencyStatusInfo, dep[1]) },
{ "WantedBy", "as", NULL, offsetof(struct DependencyStatusInfo, dep[2]) },
{ "PartOf", "as", NULL, offsetof(struct DependencyStatusInfo, dep[3]) },
{ "BoundBy", "as", NULL, offsetof(struct DependencyStatusInfo, dep[4]) },
{}
},
[DEPENDENCY_AFTER] = {
{ "After", "as", NULL, offsetof(struct DependencyStatusInfo, dep[0]) },
{}
},
[DEPENDENCY_BEFORE] = {
{ "Before", "as", NULL, offsetof(struct DependencyStatusInfo, dep[0]) },
{}
},
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_strv_free_ char **ret = NULL;
_cleanup_free_ char *dbus_path = NULL;
int i, r;
assert(bus);
assert(name);
assert(deps);
dbus_path = unit_dbus_path_from_name(name);
if (!dbus_path)
return log_oom();
r = bus_map_all_properties(bus,
"org.freedesktop.systemd1",
dbus_path,
map[arg_dependency],
BUS_MAP_STRDUP,
&error,
NULL,
&info);
if (r < 0)
return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r));
if (IN_SET(arg_dependency, DEPENDENCY_AFTER, DEPENDENCY_BEFORE)) {
*deps = info.dep[0];
return 0;
}
for (i = 0; i < 5; i++) {
r = strv_extend_strv(&ret, info.dep[i], true);
if (r < 0)
return log_oom();
info.dep[i] = strv_free(info.dep[i]);
}
*deps = TAKE_PTR(ret);
return 0;
}
static int list_dependencies_compare(char * const *a, char * const *b) { static int list_dependencies_compare(char * const *a, char * const *b) {
if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET) if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET)
return 1; return 1;
@ -5926,6 +5979,10 @@ static int show(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to expand names: %m"); return log_error_errno(r, "Failed to expand names: %m");
r = maybe_extend_with_unit_dependencies(bus, &names);
if (r < 0)
return r;
STRV_FOREACH(name, names) { STRV_FOREACH(name, names) {
_cleanup_free_ char *path; _cleanup_free_ char *path;
@ -5976,6 +6033,10 @@ static int cat(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to expand names: %m"); return log_error_errno(r, "Failed to expand names: %m");
r = maybe_extend_with_unit_dependencies(bus, &names);
if (r < 0)
return r;
(void) pager_open(arg_pager_flags); (void) pager_open(arg_pager_flags);
STRV_FOREACH(name, names) { STRV_FOREACH(name, names) {
@ -7966,6 +8027,8 @@ static int systemctl_help(void) {
" -l --full Don't ellipsize unit names on output\n" " -l --full Don't ellipsize unit names on output\n"
" -r --recursive Show unit list of host and local containers\n" " -r --recursive Show unit list of host and local containers\n"
" --reverse Show reverse dependencies with 'list-dependencies'\n" " --reverse Show reverse dependencies with 'list-dependencies'\n"
" --with-dependencies Show unit dependencies with 'status', 'cat',\n"
" 'list-units', and 'list-unit-files'.\n"
" --job-mode=MODE Specify how to deal with already queued jobs, when\n" " --job-mode=MODE Specify how to deal with already queued jobs, when\n"
" queueing a new job\n" " queueing a new job\n"
" -T --show-transaction When enqueuing a unit job, show full transaction\n" " -T --show-transaction When enqueuing a unit job, show full transaction\n"
@ -8259,6 +8322,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_BOOT_LOADER_ENTRY, ARG_BOOT_LOADER_ENTRY,
ARG_NOW, ARG_NOW,
ARG_MESSAGE, ARG_MESSAGE,
ARG_WITH_DEPENDENCIES,
ARG_WAIT, ARG_WAIT,
ARG_WHAT, ARG_WHAT,
}; };
@ -8305,6 +8369,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "plain", no_argument, NULL, ARG_PLAIN }, { "plain", no_argument, NULL, ARG_PLAIN },
{ "state", required_argument, NULL, ARG_STATE }, { "state", required_argument, NULL, ARG_STATE },
{ "recursive", no_argument, NULL, 'r' }, { "recursive", no_argument, NULL, 'r' },
{ "with-dependencies", no_argument, NULL, ARG_WITH_DEPENDENCIES },
{ "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, { "preset-mode", required_argument, NULL, ARG_PRESET_MODE },
{ "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP },
{ "boot-loader-menu", required_argument, NULL, ARG_BOOT_LOADER_MENU }, { "boot-loader-menu", required_argument, NULL, ARG_BOOT_LOADER_MENU },
@ -8665,6 +8730,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_show_transaction = true; arg_show_transaction = true;
break; break;
case ARG_WITH_DEPENDENCIES:
arg_with_dependencies = true;
break;
case ARG_WHAT: { case ARG_WHAT: {
const char *p; const char *p;

View File

@ -362,6 +362,10 @@ tests += [
[], [],
[]], []],
[['src/test/test-socket-netlink.c'],
[],
[]],
[['src/test/test-in-addr-util.c'], [['src/test/test-in-addr-util.c'],
[], [],
[]], []],

View File

@ -1043,6 +1043,168 @@ static void test_preset_multiple_instances(const char *root) {
unit_file_changes_free(changes, n_changes); unit_file_changes_free(changes, n_changes);
} }
static void verify_one(
const UnitFileInstallInfo *i,
const char *alias,
int expected,
const char *updated_name) {
int r;
static const UnitFileInstallInfo *last_info = NULL;
_cleanup_free_ char *alias2 = NULL;
if (i != last_info)
log_info("-- %s --", (last_info = i)->name);
r = unit_file_verify_alias(i, alias, &alias2);
log_info_errno(r, "alias %s ← %s: %d/%m (expected %d)%s%s%s",
i->name, alias, r, expected,
alias2 ? " [" : "", alias2 ?: "", alias2 ? "]" : "");
assert(r == expected);
/* This is is test for "instance propagation". This propagation matters mostly for WantedBy= and
* RequiredBy= settings, and less so for Alias=. The only case where it should happen is when we have
* an Alias=alias@.service an instantiated template template@instance. In that case the instance name
* should be propagated into the alias as alias@instance. */
assert(streq_ptr(alias2, updated_name));
}
static void test_verify_alias(void) {
const UnitFileInstallInfo
plain_service = { .name = (char*) "plain.service" },
bare_template = { .name = (char*) "template1@.service" },
di_template = { .name = (char*) "template2@.service",
.default_instance = (char*) "di" },
inst_template = { .name = (char*) "template3@inst.service" },
di_inst_template = { .name = (char*) "template4@inst.service",
.default_instance = (char*) "di" };
verify_one(&plain_service, "alias.service", 0, NULL);
verify_one(&plain_service, "alias.socket", -EXDEV, NULL);
verify_one(&plain_service, "alias@.service", -EXDEV, NULL);
verify_one(&plain_service, "alias@inst.service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.wants/plain.service", 0, NULL);
verify_one(&plain_service, "foo.target.wants/plain.socket", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.wants/plain@.service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.wants/service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.requires/plain.service", 0, NULL);
verify_one(&plain_service, "foo.target.requires/plain.socket", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.requires/plain@.service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.requires/service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.conf/plain.service", -EXDEV, NULL);
verify_one(&plain_service, "foo.service/plain.service", -EXDEV, NULL); /* missing dir suffix */
verify_one(&plain_service, "asdf.requires/plain.service", -EXDEV, NULL); /* invalid unit name component */
verify_one(&bare_template, "alias.service", -EXDEV, NULL);
verify_one(&bare_template, "alias.socket", -EXDEV, NULL);
verify_one(&bare_template, "alias@.socket", -EXDEV, NULL);
verify_one(&bare_template, "alias@inst.socket", -EXDEV, NULL);
/* A general alias alias@.service → template1@.service. */
verify_one(&bare_template, "alias@.service", 0, NULL);
/* Only a specific instance is aliased, see the discussion in https://github.com/systemd/systemd/pull/13119. */
verify_one(&bare_template, "alias@inst.service", 0, NULL);
verify_one(&bare_template, "foo.target.wants/plain.service", -EXDEV, NULL);
verify_one(&bare_template, "foo.target.wants/plain.socket", -EXDEV, NULL);
verify_one(&bare_template, "foo.target.wants/plain@.service", -EXDEV, NULL);
/* Name mistmatch: we cannot allow this, because plain@foo.service would be pulled in by foo.taget,
* but would not be resolvable on its own, since systemd doesn't know how to load the fragment. */
verify_one(&bare_template, "foo.target.wants/plain@foo.service", -EXDEV, NULL);
verify_one(&bare_template, "foo.target.wants/template1@foo.service", 0, NULL);
verify_one(&bare_template, "foo.target.wants/service", -EXDEV, NULL);
verify_one(&bare_template, "foo.target.requires/plain.service", -EXDEV, NULL);
verify_one(&bare_template, "foo.target.requires/plain.socket", -EXDEV, NULL);
verify_one(&bare_template, "foo.target.requires/plain@.service", -EXDEV, NULL); /* instance missing */
verify_one(&bare_template, "foo.target.requires/template1@inst.service", 0, NULL);
verify_one(&bare_template, "foo.target.requires/service", -EXDEV, NULL);
verify_one(&bare_template, "foo.target.conf/plain.service", -EXDEV, NULL);
verify_one(&bare_template, "FOO@.target.requires/plain@.service", -EXDEV, NULL); /* template name mistatch */
verify_one(&bare_template, "FOO@inst.target.requires/plain@.service", -EXDEV, NULL);
verify_one(&bare_template, "FOO@inst.target.requires/plain@inst.service", -EXDEV, NULL);
verify_one(&bare_template, "FOO@.target.requires/template1@.service", 0, NULL); /* instance propagated */
verify_one(&bare_template, "FOO@inst.target.requires/template1@.service", -EXDEV, NULL); /* instance missing */
verify_one(&bare_template, "FOO@inst.target.requires/template1@inst.service", 0, NULL); /* instance provided */
verify_one(&di_template, "alias.service", -EXDEV, NULL);
verify_one(&di_template, "alias.socket", -EXDEV, NULL);
verify_one(&di_template, "alias@.socket", -EXDEV, NULL);
verify_one(&di_template, "alias@inst.socket", -EXDEV, NULL);
verify_one(&di_template, "alias@inst.service", 0, NULL);
verify_one(&di_template, "alias@.service", 0, NULL);
verify_one(&di_template, "alias@di.service", 0, NULL);
verify_one(&di_template, "foo.target.wants/plain.service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.wants/plain.socket", -EXDEV, NULL);
verify_one(&di_template, "foo.target.wants/plain@.service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.wants/plain@di.service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.wants/template2@di.service", 0, NULL);
verify_one(&di_template, "foo.target.wants/service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.requires/plain.service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.requires/plain.socket", -EXDEV, NULL);
verify_one(&di_template, "foo.target.requires/plain@.service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.requires/plain@di.service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.requires/plain@foo.service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.requires/template2@.service", -EXDEV, NULL); /* instance missing */
verify_one(&di_template, "foo.target.requires/template2@di.service", 0, NULL);
verify_one(&di_template, "foo.target.requires/service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.conf/plain.service", -EXDEV, NULL);
verify_one(&inst_template, "alias.service", -EXDEV, NULL);
verify_one(&inst_template, "alias.socket", -EXDEV, NULL);
verify_one(&inst_template, "alias@.socket", -EXDEV, NULL);
verify_one(&inst_template, "alias@inst.socket", -EXDEV, NULL);
verify_one(&inst_template, "alias@inst.service", 0, NULL);
verify_one(&inst_template, "alias@.service", 0, "alias@inst.service");
verify_one(&inst_template, "alias@di.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.wants/plain.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.wants/plain.socket", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.wants/plain@.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.wants/plain@di.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.wants/plain@inst.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.wants/template3@foo.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.wants/template3@inst.service", 0, NULL);
verify_one(&inst_template, "bar.target.wants/service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.requires/plain.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.requires/plain.socket", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.requires/plain@.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.requires/plain@di.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.requires/plain@inst.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.requires/template3@foo.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.requires/template3@inst.service", 0, NULL);
verify_one(&inst_template, "bar.target.requires/service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.conf/plain.service", -EXDEV, NULL);
verify_one(&inst_template, "BAR@.target.requires/plain@.service", -EXDEV, NULL); /* template name mistatch */
verify_one(&inst_template, "BAR@inst.target.requires/plain@.service", -EXDEV, NULL);
verify_one(&inst_template, "BAR@inst.target.requires/plain@inst.service", -EXDEV, NULL);
verify_one(&inst_template, "BAR@.target.requires/template3@.service", -EXDEV, NULL); /* instance missing */
verify_one(&inst_template, "BAR@inst.target.requires/template3@.service", -EXDEV, NULL); /* instance missing */
verify_one(&inst_template, "BAR@inst.target.requires/template3@inst.service", 0, NULL); /* instance provided */
verify_one(&inst_template, "BAR@inst.target.requires/template3@ins2.service", -EXDEV, NULL); /* instance mismatch */
/* explicit alias overrides DefaultInstance */
verify_one(&di_inst_template, "alias.service", -EXDEV, NULL);
verify_one(&di_inst_template, "alias.socket", -EXDEV, NULL);
verify_one(&di_inst_template, "alias@.socket", -EXDEV, NULL);
verify_one(&di_inst_template, "alias@inst.socket", -EXDEV, NULL);
verify_one(&di_inst_template, "alias@inst.service", 0, NULL);
verify_one(&di_inst_template, "alias@.service", 0, "alias@inst.service");
verify_one(&di_inst_template, "alias@di.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.wants/plain.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.wants/plain.socket", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.wants/plain@.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.wants/plain@di.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.wants/template4@foo.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.wants/template4@inst.service", 0, NULL);
verify_one(&di_inst_template, "goo.target.wants/template4@di.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.wants/service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.requires/plain.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.requires/plain.socket", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.requires/plain@.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.requires/plain@di.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.requires/plain@inst.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.requires/template4@foo.service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.requires/template4@inst.service", 0, NULL);
verify_one(&di_inst_template, "goo.target.requires/service", -EXDEV, NULL);
verify_one(&di_inst_template, "goo.target.conf/plain.service", -EXDEV, NULL);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
char root[] = "/tmp/rootXXXXXX"; char root[] = "/tmp/rootXXXXXX";
const char *p; const char *p;
@ -1080,5 +1242,7 @@ int main(int argc, char *argv[]) {
assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
test_verify_alias();
return 0; return 0;
} }

View File

@ -0,0 +1,227 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "tests.h"
#include "socket-netlink.h"
#include "string-util.h"
static void test_socket_address_parse_one(const char *in, int ret, int family, const char *expected) {
SocketAddress a;
_cleanup_free_ char *out = NULL;
int r;
r = socket_address_parse(&a, in);
if (r >= 0)
assert_se(socket_address_print(&a, &out) >= 0);
log_info("\"%s\" → %s → \"%s\" (expect \"%s\")", in,
r >= 0 ? "" : "", empty_to_dash(out), r >= 0 ? expected ?: in : "-");
assert_se(r == ret);
if (r >= 0) {
assert_se(a.sockaddr.sa.sa_family == family);
assert_se(streq(out, expected ?: in));
}
}
static void test_socket_address_parse(void) {
log_info("/* %s */", __func__);
test_socket_address_parse_one("junk", -EINVAL, 0, NULL);
test_socket_address_parse_one("192.168.1.1", -EINVAL, 0, NULL);
test_socket_address_parse_one(".168.1.1", -EINVAL, 0, NULL);
test_socket_address_parse_one("989.168.1.1", -EINVAL, 0, NULL);
test_socket_address_parse_one("192.168.1.1:65536", -ERANGE, 0, NULL);
test_socket_address_parse_one("192.168.1.1:0", -EINVAL, 0, NULL);
test_socket_address_parse_one("0", -EINVAL, 0, NULL);
test_socket_address_parse_one("65536", -ERANGE, 0, NULL);
const int default_family = socket_ipv6_is_supported() ? AF_INET6 : AF_INET;
test_socket_address_parse_one("65535", 0, default_family, "[::]:65535");
/* The checks below will pass even if ipv6 is disabled in
* kernel. The underlying glibc's inet_pton() is just a string
* parser and doesn't make any syscalls. */
test_socket_address_parse_one("[::1]", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]8888", -EINVAL, 0, NULL);
test_socket_address_parse_one("::1", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]:0", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]:65536", -ERANGE, 0, NULL);
test_socket_address_parse_one("[a:b:1]:8888", -EINVAL, 0, NULL);
test_socket_address_parse_one("8888", 0, default_family, "[::]:8888");
test_socket_address_parse_one("[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888", 0, AF_INET6,
"[2001:db8:0:85a3::ac1f:8001]:8888");
test_socket_address_parse_one("[::1]:8888", 0, AF_INET6, NULL);
test_socket_address_parse_one("192.168.1.254:8888", 0, AF_INET, NULL);
test_socket_address_parse_one("/foo/bar", 0, AF_UNIX, NULL);
test_socket_address_parse_one("/", 0, AF_UNIX, NULL);
test_socket_address_parse_one("@abstract", 0, AF_UNIX, NULL);
{
char aaa[SUN_PATH_LEN + 1] = "@";
memset(aaa + 1, 'a', SUN_PATH_LEN - 1);
char_array_0(aaa);
test_socket_address_parse_one(aaa, -EINVAL, 0, NULL);
aaa[SUN_PATH_LEN - 1] = '\0';
test_socket_address_parse_one(aaa, 0, AF_UNIX, NULL);
}
test_socket_address_parse_one("vsock:2:1234", 0, AF_VSOCK, NULL);
test_socket_address_parse_one("vsock::1234", 0, AF_VSOCK, NULL);
test_socket_address_parse_one("vsock:2:1234x", -EINVAL, 0, NULL);
test_socket_address_parse_one("vsock:2x:1234", -EINVAL, 0, NULL);
test_socket_address_parse_one("vsock:2", -EINVAL, 0, NULL);
}
static void test_socket_address_parse_netlink(void) {
SocketAddress a;
log_info("/* %s */", __func__);
assert_se(socket_address_parse_netlink(&a, "junk") < 0);
assert_se(socket_address_parse_netlink(&a, "") < 0);
assert_se(socket_address_parse_netlink(&a, "route") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 0);
assert_se(a.protocol == NETLINK_ROUTE);
assert_se(socket_address_parse_netlink(&a, "route") >= 0);
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 10);
assert_se(a.protocol == NETLINK_ROUTE);
/* With spaces and tabs */
assert_se(socket_address_parse_netlink(&a, " kobject-uevent ") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 0);
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
assert_se(socket_address_parse_netlink(&a, " \t kobject-uevent \t 10") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 10);
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 10);
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
/* trailing space is not supported */
assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10 ") < 0);
/* Group must be unsigned */
assert_se(socket_address_parse_netlink(&a, "kobject-uevent -1") < 0);
/* oss-fuzz #6884 */
assert_se(socket_address_parse_netlink(&a, "\xff") < 0);
}
static void test_socket_address_equal(void) {
SocketAddress a, b;
log_info("/* %s */", __func__);
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "192.168.1.1:888") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "192.16.1.1:8888") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "8888") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "/foo/bar/") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "192.168.1.1:8888") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
assert_se(socket_address_parse(&b, "/foo/bar") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
assert_se(socket_address_parse(&b, "[::1]:8888") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "@abstract") >= 0);
assert_se(socket_address_parse(&b, "@abstract") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse_netlink(&a, "firewall") >= 0);
assert_se(socket_address_parse_netlink(&b, "firewall") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
assert_se(socket_address_parse(&b, "vsock:2:1234") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&b, "vsock:2:1235") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&b, "vsock:3:1234") >= 0);
assert_se(!socket_address_equal(&a, &b));
}
static void test_socket_address_get_path(void) {
SocketAddress a;
log_info("/* %s */", __func__);
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(!socket_address_get_path(&a));
assert_se(socket_address_parse(&a, "@abstract") >= 0);
assert_se(!socket_address_get_path(&a));
assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
assert_se(!socket_address_get_path(&a));
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
assert_se(streq(socket_address_get_path(&a), "/foo/bar"));
assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
assert_se(!socket_address_get_path(&a));
}
static void test_socket_address_is(void) {
SocketAddress a;
log_info("/* %s */", __func__);
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_is(&a, "192.168.1.1:8888", SOCK_STREAM));
assert_se(!socket_address_is(&a, "route", SOCK_STREAM));
assert_se(!socket_address_is(&a, "192.168.1.1:8888", SOCK_RAW));
}
static void test_socket_address_is_netlink(void) {
SocketAddress a;
log_info("/* %s */", __func__);
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
assert_se(socket_address_is_netlink(&a, "route 10"));
assert_se(!socket_address_is_netlink(&a, "192.168.1.1:8888"));
assert_se(!socket_address_is_netlink(&a, "route 1"));
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_socket_address_parse();
test_socket_address_parse_netlink();
test_socket_address_equal();
test_socket_address_get_path();
test_socket_address_is();
test_socket_address_is_netlink();
return 0;
}

View File

@ -17,11 +17,14 @@
#include "macro.h" #include "macro.h"
#include "missing_network.h" #include "missing_network.h"
#include "process-util.h" #include "process-util.h"
#include "socket-netlink.h"
#include "socket-util.h" #include "socket-util.h"
#include "string-util.h" #include "string-util.h"
#include "tests.h" #include "tests.h"
#include "tmpfile-util.h" #include "tmpfile-util.h"
assert_cc(SUN_PATH_LEN == 108);
static void test_ifname_valid(void) { static void test_ifname_valid(void) {
log_info("/* %s */", __func__); log_info("/* %s */", __func__);
@ -48,82 +51,6 @@ static void test_ifname_valid(void) {
assert(ifname_valid_full("xxxxxxxxxxxxxxxx", true)); assert(ifname_valid_full("xxxxxxxxxxxxxxxx", true));
} }
static void test_socket_address_parse_one(const char *in, int ret, int family, const char *expected) {
SocketAddress a;
_cleanup_free_ char *out = NULL;
int r;
r = socket_address_parse(&a, in);
if (r >= 0)
assert_se(socket_address_print(&a, &out) >= 0);
log_info("\"%s\" → %s → \"%s\" (expect \"%s\")", in,
r >= 0 ? "" : "", empty_to_dash(out), r >= 0 ? expected ?: in : "-");
assert_se(r == ret);
if (r >= 0) {
assert_se(a.sockaddr.sa.sa_family == family);
assert_se(streq(out, expected ?: in));
}
}
#define SUN_PATH_LEN (sizeof(((struct sockaddr_un){}).sun_path))
assert_cc(SUN_PATH_LEN == 108);
static void test_socket_address_parse(void) {
log_info("/* %s */", __func__);
test_socket_address_parse_one("junk", -EINVAL, 0, NULL);
test_socket_address_parse_one("192.168.1.1", -EINVAL, 0, NULL);
test_socket_address_parse_one(".168.1.1", -EINVAL, 0, NULL);
test_socket_address_parse_one("989.168.1.1", -EINVAL, 0, NULL);
test_socket_address_parse_one("192.168.1.1:65536", -ERANGE, 0, NULL);
test_socket_address_parse_one("192.168.1.1:0", -EINVAL, 0, NULL);
test_socket_address_parse_one("0", -EINVAL, 0, NULL);
test_socket_address_parse_one("65536", -ERANGE, 0, NULL);
const int default_family = socket_ipv6_is_supported() ? AF_INET6 : AF_INET;
test_socket_address_parse_one("65535", 0, default_family, "[::]:65535");
/* The checks below will pass even if ipv6 is disabled in
* kernel. The underlying glibc's inet_pton() is just a string
* parser and doesn't make any syscalls. */
test_socket_address_parse_one("[::1]", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]8888", -EINVAL, 0, NULL);
test_socket_address_parse_one("::1", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]:0", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]:65536", -ERANGE, 0, NULL);
test_socket_address_parse_one("[a:b:1]:8888", -EINVAL, 0, NULL);
test_socket_address_parse_one("8888", 0, default_family, "[::]:8888");
test_socket_address_parse_one("[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888", 0, AF_INET6,
"[2001:db8:0:85a3::ac1f:8001]:8888");
test_socket_address_parse_one("[::1]:8888", 0, AF_INET6, NULL);
test_socket_address_parse_one("192.168.1.254:8888", 0, AF_INET, NULL);
test_socket_address_parse_one("/foo/bar", 0, AF_UNIX, NULL);
test_socket_address_parse_one("/", 0, AF_UNIX, NULL);
test_socket_address_parse_one("@abstract", 0, AF_UNIX, NULL);
{
char aaa[SUN_PATH_LEN + 1] = "@";
memset(aaa + 1, 'a', SUN_PATH_LEN - 1);
char_array_0(aaa);
test_socket_address_parse_one(aaa, -EINVAL, 0, NULL);
aaa[SUN_PATH_LEN - 1] = '\0';
test_socket_address_parse_one(aaa, 0, AF_UNIX, NULL);
}
test_socket_address_parse_one("vsock:2:1234", 0, AF_VSOCK, NULL);
test_socket_address_parse_one("vsock::1234", 0, AF_VSOCK, NULL);
test_socket_address_parse_one("vsock:2:1234x", -EINVAL, 0, NULL);
test_socket_address_parse_one("vsock:2x:1234", -EINVAL, 0, NULL);
test_socket_address_parse_one("vsock:2", -EINVAL, 0, NULL);
}
static void test_socket_print_unix_one(const char *in, size_t len_in, const char *expected) { static void test_socket_print_unix_one(const char *in, size_t len_in, const char *expected) {
_cleanup_free_ char *out = NULL, *c = NULL; _cleanup_free_ char *out = NULL, *c = NULL;
@ -157,141 +84,6 @@ static void test_socket_print_unix(void) {
test_socket_print_unix_one("\0\a\b\n\255", 6, "@\\a\\b\\n\\255\\000"); test_socket_print_unix_one("\0\a\b\n\255", 6, "@\\a\\b\\n\\255\\000");
} }
static void test_socket_address_parse_netlink(void) {
SocketAddress a;
log_info("/* %s */", __func__);
assert_se(socket_address_parse_netlink(&a, "junk") < 0);
assert_se(socket_address_parse_netlink(&a, "") < 0);
assert_se(socket_address_parse_netlink(&a, "route") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 0);
assert_se(a.protocol == NETLINK_ROUTE);
assert_se(socket_address_parse_netlink(&a, "route") >= 0);
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 10);
assert_se(a.protocol == NETLINK_ROUTE);
/* With spaces and tabs */
assert_se(socket_address_parse_netlink(&a, " kobject-uevent ") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 0);
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
assert_se(socket_address_parse_netlink(&a, " \t kobject-uevent \t 10") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 10);
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10") >= 0);
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
assert_se(a.sockaddr.nl.nl_groups == 10);
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
/* trailing space is not supported */
assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10 ") < 0);
/* Group must be unsigned */
assert_se(socket_address_parse_netlink(&a, "kobject-uevent -1") < 0);
/* oss-fuzz #6884 */
assert_se(socket_address_parse_netlink(&a, "\xff") < 0);
}
static void test_socket_address_equal(void) {
SocketAddress a, b;
log_info("/* %s */", __func__);
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "192.168.1.1:888") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "192.16.1.1:8888") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "8888") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "/foo/bar/") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "192.168.1.1:8888") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
assert_se(socket_address_parse(&b, "/foo/bar") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
assert_se(socket_address_parse(&b, "[::1]:8888") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "@abstract") >= 0);
assert_se(socket_address_parse(&b, "@abstract") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse_netlink(&a, "firewall") >= 0);
assert_se(socket_address_parse_netlink(&b, "firewall") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
assert_se(socket_address_parse(&b, "vsock:2:1234") >= 0);
assert_se(socket_address_equal(&a, &b));
assert_se(socket_address_parse(&b, "vsock:2:1235") >= 0);
assert_se(!socket_address_equal(&a, &b));
assert_se(socket_address_parse(&b, "vsock:3:1234") >= 0);
assert_se(!socket_address_equal(&a, &b));
}
static void test_socket_address_get_path(void) {
SocketAddress a;
log_info("/* %s */", __func__);
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(!socket_address_get_path(&a));
assert_se(socket_address_parse(&a, "@abstract") >= 0);
assert_se(!socket_address_get_path(&a));
assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
assert_se(!socket_address_get_path(&a));
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
assert_se(streq(socket_address_get_path(&a), "/foo/bar"));
assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
assert_se(!socket_address_get_path(&a));
}
static void test_socket_address_is(void) {
SocketAddress a;
log_info("/* %s */", __func__);
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_is(&a, "192.168.1.1:8888", SOCK_STREAM));
assert_se(!socket_address_is(&a, "route", SOCK_STREAM));
assert_se(!socket_address_is(&a, "192.168.1.1:8888", SOCK_RAW));
}
static void test_socket_address_is_netlink(void) {
SocketAddress a;
log_info("/* %s */", __func__);
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
assert_se(socket_address_is_netlink(&a, "route 10"));
assert_se(!socket_address_is_netlink(&a, "192.168.1.1:8888"));
assert_se(!socket_address_is_netlink(&a, "route 1"));
}
static void test_in_addr_is_null(void) { static void test_in_addr_is_null(void) {
union in_addr_union i = {}; union in_addr_union i = {};
@ -876,13 +668,7 @@ int main(int argc, char *argv[]) {
test_ifname_valid(); test_ifname_valid();
test_socket_address_parse();
test_socket_print_unix(); test_socket_print_unix();
test_socket_address_parse_netlink();
test_socket_address_equal();
test_socket_address_get_path();
test_socket_address_is();
test_socket_address_is_netlink();
test_in_addr_is_null(); test_in_addr_is_null();
test_in_addr_prefix_intersect(); test_in_addr_prefix_intersect();

View File

@ -78,6 +78,10 @@ static void test_unit_name_is_valid(void) {
test_unit_name_is_valid_one("foo@%%i.service", UNIT_NAME_INSTANCE, false); test_unit_name_is_valid_one("foo@%%i.service", UNIT_NAME_INSTANCE, false);
test_unit_name_is_valid_one("foo@%%i%f.service", UNIT_NAME_INSTANCE, false); test_unit_name_is_valid_one("foo@%%i%f.service", UNIT_NAME_INSTANCE, false);
test_unit_name_is_valid_one("foo@%F.service", UNIT_NAME_INSTANCE, false); test_unit_name_is_valid_one("foo@%F.service", UNIT_NAME_INSTANCE, false);
test_unit_name_is_valid_one("foo.target.wants/plain.service", UNIT_NAME_ANY, false);
test_unit_name_is_valid_one("foo.target.conf/foo.conf", UNIT_NAME_ANY, false);
test_unit_name_is_valid_one("foo.target.requires/plain.socket", UNIT_NAME_ANY, false);
} }
static void test_unit_name_replace_instance_one(const char *pattern, const char *repl, const char *expected, int ret) { static void test_unit_name_replace_instance_one(const char *pattern, const char *repl, const char *expected, int ret) {

View File

@ -4,6 +4,7 @@
#include "format-util.h" #include "format-util.h"
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
#include "memory-util.h"
#include "path-util.h" #include "path-util.h"
#include "string-util.h" #include "string-util.h"
#include "user-util.h" #include "user-util.h"
@ -287,12 +288,41 @@ static void test_make_salt(void) {
} }
static void test_in_gid(void) { static void test_in_gid(void) {
assert(in_gid(getgid()) >= 0); assert(in_gid(getgid()) >= 0);
assert(in_gid(getegid()) >= 0); assert(in_gid(getegid()) >= 0); assert(in_gid(TTY_GID) == 0); /* The TTY gid is for owning ttys, it would be really really weird if we were in it. */
}
assert(in_gid(GID_INVALID) < 0); static void test_gid_lists_ops(void) {
assert(in_gid(TTY_GID) == 0); /* The TTY gid is for owning ttys, it would be really really weird if we were in it. */ static const gid_t l1[] = { 5, 10, 15, 20, 25};
static const gid_t l2[] = { 1, 2, 3, 15, 20, 25};
static const gid_t l3[] = { 5, 10, 15, 20, 25, 26, 27};
static const gid_t l4[] = { 25, 26, 20, 15, 5, 27, 10};
static const gid_t result1[] = {1, 2, 3, 5, 10, 15, 20, 25, 26, 27};
static const gid_t result2[] = {5, 10, 15, 20, 25, 26, 27};
_cleanup_free_ gid_t *gids = NULL;
_cleanup_free_ gid_t *res1 = NULL;
_cleanup_free_ gid_t *res2 = NULL;
_cleanup_free_ gid_t *res3 = NULL;
_cleanup_free_ gid_t *res4 = NULL;
int nresult;
nresult = merge_gid_lists(l2, ELEMENTSOF(l2), l3, ELEMENTSOF(l3), &res1);
assert_se(memcmp_nn(res1, nresult, result1, ELEMENTSOF(result1)) == 0);
nresult = merge_gid_lists(NULL, 0, l2, ELEMENTSOF(l2), &res2);
assert_se(memcmp_nn(res2, nresult, l2, ELEMENTSOF(l2)) == 0);
nresult = merge_gid_lists(l1, ELEMENTSOF(l1), l1, ELEMENTSOF(l1), &res3);
assert_se(memcmp_nn(l1, ELEMENTSOF(l1), res3, nresult) == 0);
nresult = merge_gid_lists(l1, ELEMENTSOF(l1), l4, ELEMENTSOF(l4), &res4);
assert_se(memcmp_nn(result2, ELEMENTSOF(result2), res4, nresult) == 0);
nresult = getgroups_alloc(&gids);
assert_se(nresult >= 0 || nresult == -EINVAL || nresult == -ENOMEM);
assert_se(gids);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -330,6 +360,7 @@ int main(int argc, char *argv[]) {
test_make_salt(); test_make_salt();
test_in_gid(); test_in_gid();
test_gid_lists_ops();
return 0; return 0;
} }

View File

@ -829,7 +829,7 @@ static int show_timesync(int argc, char **argv, void *userdata) {
return 0; return 0;
} }
static int parse_ifindex_bus(sd_bus *bus, const char *str, int *ret) { static int parse_ifindex_bus(sd_bus *bus, const char *str) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int32_t i; int32_t i;
@ -837,11 +837,11 @@ static int parse_ifindex_bus(sd_bus *bus, const char *str, int *ret) {
assert(bus); assert(bus);
assert(str); assert(str);
assert(ret);
r = parse_ifindex(str, ret); r = parse_ifindex(str);
if (r >= 0) if (r > 0)
return 0; return r;
assert(r < 0);
r = sd_bus_call_method( r = sd_bus_call_method(
bus, bus,
@ -859,8 +859,7 @@ static int parse_ifindex_bus(sd_bus *bus, const char *str, int *ret) {
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
*ret = i; return i;
return 0;
} }
static int verb_ntp_servers(int argc, char **argv, void *userdata) { static int verb_ntp_servers(int argc, char **argv, void *userdata) {
@ -871,9 +870,9 @@ static int verb_ntp_servers(int argc, char **argv, void *userdata) {
assert(bus); assert(bus);
r = parse_ifindex_bus(bus, argv[1], &ifindex); ifindex = parse_ifindex_bus(bus, argv[1]);
if (r < 0) if (ifindex < 0)
return r; return ifindex;
polkit_agent_open_if_enabled(arg_transport, arg_ask_password); polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
@ -909,9 +908,9 @@ static int verb_revert(int argc, char **argv, void *userdata) {
assert(bus); assert(bus);
r = parse_ifindex_bus(bus, argv[1], &ifindex); ifindex = parse_ifindex_bus(bus, argv[1]);
if (r < 0) if (ifindex < 0)
return r; return ifindex;
polkit_agent_open_if_enabled(arg_transport, arg_ask_password); polkit_agent_open_if_enabled(arg_transport, arg_ask_password);