Compare commits
47 Commits
e2f7943d22
...
89f3ba7995
Author | SHA1 | Date |
---|---|---|
Zbigniew Jędrzejewski-Szmek | 89f3ba7995 | |
Lennart Poettering | 180f7c26aa | |
Lennart Poettering | f8349d2fa5 | |
Lennart Poettering | 0bdd282a4e | |
Lennart Poettering | 6e47cac0aa | |
Lennart Poettering | 765d88698f | |
Lennart Poettering | 04d8507f68 | |
Lennart Poettering | 200427cf7c | |
Lennart Poettering | 0b9da3d9e8 | |
Zeyu DONG | 61c0ac0924 | |
Timo Schlüßler | 23b3921663 | |
Timo Schlüßler | 3ac9cac7f7 | |
Lennart Poettering | 575a915a74 | |
Zbigniew Jędrzejewski-Szmek | 5749698031 | |
Lennart Poettering | f9951b0cf0 | |
Lennart Poettering | 151a7133cd | |
Dariusz Gadomski | c6cecb744b | |
Dariusz Gadomski | afb11bf1b8 | |
Dariusz Gadomski | 3bb39ea936 | |
Dariusz Gadomski | 0c5d667932 | |
Zbigniew Jędrzejewski-Szmek | 117caf3765 | |
Zbigniew Jędrzejewski-Szmek | 8571210a21 | |
Zbigniew Jędrzejewski-Szmek | 957d9df388 | |
Zbigniew Jędrzejewski-Szmek | d308bb99d2 | |
Zbigniew Jędrzejewski-Szmek | fc2ea97ad0 | |
Zbigniew Jędrzejewski-Szmek | 5c3fa98db6 | |
Zbigniew Jędrzejewski-Szmek | 955bb7fac3 | |
Zbigniew Jędrzejewski-Szmek | bad7cecc0a | |
Zbigniew Jędrzejewski-Szmek | 231d9de1e3 | |
Zbigniew Jędrzejewski-Szmek | 9030b50a7b | |
Zbigniew Jędrzejewski-Szmek | 597da51bae | |
Zbigniew Jędrzejewski-Szmek | 2e93770fd8 | |
Zbigniew Jędrzejewski-Szmek | 1bf1558552 | |
Zbigniew Jędrzejewski-Szmek | 3f57bc2267 | |
Zbigniew Jędrzejewski-Szmek | 9a4f9e69e1 | |
Kevin Kuehler | a602a0b44b | |
Kevin Kuehler | e9c387c829 | |
Lennart Poettering | 7cce68e1e0 | |
Lennart Poettering | 8403219fc1 | |
Lennart Poettering | 08b1f5c7d1 | |
Lennart Poettering | 4eaf0d9401 | |
Lennart Poettering | f3dab34d22 | |
Kevin Kuehler | 411975ce63 | |
Zbigniew Jędrzejewski-Szmek | b59817b199 | |
Zbigniew Jędrzejewski-Szmek | f9ef25a483 | |
Zbigniew Jędrzejewski-Szmek | aa0f357fd8 | |
Jérémy Rosen | 3d0205f28b |
|
@ -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).
|
|
@ -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[0][0]='@' Logic](https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons) | /proc marking | yes | yes | mdadm | yes | - | no |
|
| [argv[0][0]='@' 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 |
|
||||||
|
|
|
@ -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)
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
|
@ -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;
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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'],
|
||||||
[],
|
[],
|
||||||
[]],
|
[]],
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue