diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2018-07-19 12:42:57 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2018-07-19 12:42:57 -0400 |
commit | 52f5927b80febce2366e23fa4bce629e8df44efb (patch) | |
tree | da608743f96af21ccd9acdfe71251ca5c472595f | |
parent | c9bb7ae80d1a888cd717c891f4be1b7f3030772f (diff) | |
parent | 361d6b31f68cc22498d478fb4709391e83ab8c4c (diff) |
Merge tag 'systemd/v238.133-4.parabola1' into systemd/parabola
54 files changed, 919 insertions, 289 deletions
@@ -232,14 +232,11 @@ USERS AND GROUPS: groups "wheel" and "adm" will be given read-only access to journal files using systemd-tmpfiles.service. - The journal gateway daemon requires the - "systemd-journal-gateway" system user and group to + The journal remote daemon requires the + "systemd-journal-remote" system user and group to exist. During execution this network facing service will drop privileges and assume this uid/gid for security reasons. - Similarly, the NTP daemon requires the "systemd-timesync" system - user and group to exist. - Similarly, the network management daemon requires the "systemd-network" system user and group to exist. @@ -275,7 +272,7 @@ NSS: passwd: compat mymachines systemd group: compat mymachines systemd - hosts: files mymachines resolve myhostname + hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname SYSV INIT.D SCRIPTS: When calling "systemctl enable/disable/is-enabled" on a unit which is a diff --git a/doc/CGROUP_DELEGATION.md b/doc/CGROUP_DELEGATION.md new file mode 100644 index 0000000000..619c871212 --- /dev/null +++ b/doc/CGROUP_DELEGATION.md @@ -0,0 +1,445 @@ +# Control Group APIs and Delegation + +*Intended audience: hackers working on userspace subsystems that require direct +cgroup access, such as container managers and similar.* + +So you are wondering about resource management with systemd, you know Linux +control groups (cgroups) a bit and are trying to integrate your software with +what systemd has to offer there. Here's a bit of documentation about the +concepts and interfaces involved with this. + +What's described here has been part of systemd and documented since v205 +times. However, it has been updated and improved substantially since, even +though the concepts stayed mostly the same. This is an attempt to provide more +comprehensive up-to-date information about all this, particular in light of the +poor implementations of the components interfacing with systemd of current +container managers. + +Before you read on, please make sure you read the low-level [kernel +documentation about +cgroupsv2](https://www.kernel.org/doc/Documentation/cgroup-v2.txt). This +documentation then adds in the higher-level view from systemd. + +This document augments the existing documentation we already have: + +* [The New Control Group Interfaces](https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/) +* [Writing VM and Container Managers](https://www.freedesktop.org/wiki/Software/systemd/writing-vm-managers/) + +These wiki documents are not as up to date as they should be, currently, but +the basic concepts still fully apply. You should read them too, if you do something +with cgroups and systemd, in particular as they shine more light on the various +D-Bus APIs provided. (That said, sooner or later we should probably fold that +wiki documentation into this very document, too.) + +## Two Key Design Rules + +Much of the philosophy behind these concepts is based on a couple of basic +design ideas of cgroupsv2 (which we however try to adapt as far as we can to +cgroupsv1 too). Specifically two cgroupsv2 rules are the most relevant: + +1. The **no-processes-in-inner-nodes** rule: this means that it's not permitted +to have processes directly attached to a cgroup that also has child cgroups and +vice versa. A cgroup is either an inner node or a leaf node of the tree, and if +it's an inner node it may not contain processes directly, and if it's a leaf +node then it may not have child cgroups. (Note that there are some minor +exceptions to this rule, though. i.e. the root cgroup is special and allows +both processes and children — which is used in particular to maintain kernel +threads.) + +2. The **single-writer** rule: this means that each cgroup only has a single +writer, i.e. a single process managing it. It's OK if different cgroups have +different processes managing them. However, only a single process should own a +specific cgroup, and when it does that ownership is exclusive, and nothing else +should manipulate it at the same time. This rule ensures that various pieces of +software don't step on each other's toes constantly. + +These two rules have various effects. For example, one corollary of this is: if +your container manager creates and manages cgroups in the system's root cgroup +you violate rule #2, as the root cgroup is managed by systemd and hence off +limits to everybody else. + +Note that rule #1 is generally enforced by the kernel if cgroupsv2 is used: as +soon as you add a process to a cgroup it is ensured the rule is not +violated. On cgroupsv1 this rule didn't exist, and hence isn't enforced, even +though it's a good thing to follow it then too. Rule #2 is not enforced on +either cgroupsv1 nor cgroupsv2 (this is UNIX after all, in the general case +root can do anything, modulo SELinux and friends), but if you ignore it you'll +be in constant pain as various pieces of software will fight over cgroup +ownership. + +Note that cgroupsv1 is currently the most deployed implementation of all of +this, even though it's semantically broken in many ways, and in many cases +doesn't actually do what people think it does. cgroupsv2 is where things are +going, and most new kernel features in this area are only added to cgroupsv2, +and not cgroupsv1 anymore. For example cgroupsv2 provides proper cgroup-empty +notifications, has support for all kinds of per-cgroup BPF magic, supports +secure delegation of cgroup trees to less privileged processes and so on, which +all are not available on cgroupsv1. + +## Three Different Tree Setups 🌳 + +systemd supports three different modes how cgroups are set up. Specifically: + +1. **Unified** — this is the simplest mode, and exposes a pure cgroupsv2 +logic. In this mode `/sys/fs/cgroup` is the only mounted cgroup API file system +and all available controllers are exclusively exposed through it. + +2. **Legacy** — this is the traditional cgroupsv1 mode. In this mode the +various controllers each get their own cgroup file system mounted to +`/sys/fs/cgroup/<controller>/`. On top of that systemd manages its own cgroup +hierarchy for managing purposes as `/sys/fs/cgroup/systemd/`. + +3. **Hybrid** — this is a hybrid between the unified and legacy mode. It's set +up mostly like legacy, except that there's also an additional hierarchy +`/sys/fs/cgroup/unified/` that contains the cgroupsv2 hierarchy. In this mode +compatibility with cgroupsv1 is retained while some cgroupsv2 features are +available too. This mode is a stopgap. Don't bother with this too much unless +you have too much free time. + +To say this clearly, legacy and hybrid modes have no future. If you develop +software today and don't focus on the unified mode, then you are writing +software for yesterday, not tomorrow. They are primarily supported for +compatibility reasons and will not receive new features. Sorry. + +Superficially, in legacy and hybrid modes it might appear that the parallel +cgroup hierarchies for each controller are orthogonal from each other. In +systemd they are not: the hierarchies of all controllers are always kept in +sync (at least mostly: sub-trees might be suppressed in certain hierarchies if +no controller usage is required for them). The fact that systemd keeps these +hierarchies in sync means that the legacy and hybrid hierarchies are +conceptually very close to the unified hierarchy. In particular this allows us +talk of one specific cgroup and actually mean the same cgroup in all available +controller hierarchies. e.g. if we talk about the cgroup `/foo/bar/` then we +actually mean `/sys/fs/cgroup/cpu/foo/bar/` as well as +`/sys/fs/cgroup/memory/foo/bar/`, `/sys/fs/cgroup/pids/foo/bar/`, and so on, in +one. Note that in cgroupsv2 the controller hierarchies aren't orthogonal, hence +thinking about them as orthogonal won't help you in the long run anyway. + +If you wonder how to detect which of these three modes is currently used, use +`statfs()` on `/sys/fs/cgroup/`. If it reports `CGROUP2_SUPER_MAGIC` in its +`.f_type` field, then you are in unified mode. If it reports `TMPFS_MAGIC` then +you are either in legacy or hybrid mode. To distuingish these two cases, run +`statfs()` again on `/sys/fs/cgroup/unified/`. If that succeeds and reports +`CGROUP2_SUPER_MAGIC` you are in hybrid mode, otherwise not. + +## systemd's Unit Types + +The low-level kernel cgroups feature is exposed in systemd in three different +"unit" types. Specifically: + +1. 💼 The `.service` unit type. This unit type is for units encapsulating + processes systemd itself starts. Units of these types have cgroups that are + the leaves of the cgroup tree the systemd instance manages (though possibly + they might contain a sub-tree of their own managed by something else, made + possible by the concept of delegation, see below). Service units are usually + instantiated based on a unit file on disk that describes the command line to + invoke and other properties of the service. However, service units may also + be declared and started programmatically at runtime through a D-Bus API + (which is called *transient* services). + +2. 👓 The `.scope` unit type. This is very similar to `.service`. The main + difference: the processes the units of this type encapsulate are forked off + by some unrelated manager process, and that manager asked systemd to expose + them as a unit. Unlike services, scopes can only be declared and started + programmatically, i.e. are always transient. That's because they encapsulate + processes forked off by something else, i.e. existing runtime objects, and + hence cannot really be defined fully in 'offline' concepts such as unit + files. + +3. 🔪 The `.slice` unit type. Units of this type do not directly contain any + processes. Units of this type are the inner nodes of part of the cgroup tree + the systemd instance manages. Much like services, slices can be defined + either on disk with unit files or programmatically as transient units. + +Slices expose the trunk and branches of a tree, and scopes and services are +attached to those branches as leaves. The idea is that scopes and services can +be moved around though, i.e. assigned to a different slice if needed. + +The naming of slice units directly maps to the cgroup tree path. This is not +the case for service and scope units however. A slice named `foo-bar-baz.slice` +maps to a cgroup `/foo.slice/foo-bar.slice/foo-bar-baz.slice/`. A service +`quux.service` which is attached to the slice `foo-bar-baz.slice` maps to the +cgroup `/foo.slice/foo-bar.slice/foo-bar-baz.slice/quux.service/`. + +By default systemd sets up four slice units: + +1. `-.slice` is the root slice. i.e. the parent of everything else. On the host + system it maps directly to the top-level directory of cgroupsv2. + +2. `system.slice` is where system services are by default placed, unless + configured otherwise. + +3. `user.slice` is where user sessions are placed. Each user gets a slice of + its own below that. + +4. `machines.slice` is where VMs and containers are supposed to be + placed. `systemd-nspawn` makes use of this by default, and you're very welcome + to place your containers and VMs there too if you hack on managers for those. + +Users may define any amount of additional slices they like though, the four +above are just the defaults. + +## Delegation + +Container managers and suchlike often want to control cgroups directly using +the raw kernel APIs. That's entirely fine and supported, as long as proper +*delegation* is followed. Delegation is a concept we inherited from cgroupsv2, +but we expose it on cgroupsv1 too. Delegation means that some parts of the +cgroup tree may be managed by different managers than others. As long as it is +clear which manager manages which part of the tree each one can do within its +sub-graph of the tree whatever it wants. + +Only sub-trees can be delegated (though whoever decides to request a sub-tree +can delegate sub-sub-trees further to somebody else if they like +it). Delegation takes place at a specific cgroup: in systemd there's a +`Delegate=` property you can set for a service or scope unit. If you do, it's +the cut-off point for systemd's cgroup management: the unit itself is managed +by systemd, i.e. all its attributes are managed exclusively by systemd, however +your program may create/remove sub-cgroups inside it freely, and those then +become exclusive property of your program, systemd won't touch them — all +attributes of *those* sub-cgroups can be manipulated freely and exclusively by +your program. + +By turning on the `Delegate=` property for a scope or service you get a few +guarantees: + +1. systemd won't fiddle with your sub-tree of the cgroup tree anymore. It won't + change attributes of any cgroups below it, nor will it create or remove any + cgroups thereunder, nor migrate processes across the boundaries of that + sub-tree as it deems useful anymore. + +2. If your service makes use of the `User=` functionality, then the sub-tree + will be `chown()`ed to the indicated user so that it can correctly create + cgroups below it. Note however that systemd will do that only in the unified + hierarchy (in unified and hybrid mode) as well as on systemd's own private + hierarchy (in legacy and hybrid mode). It won't pass ownership of the legacy + controller hierarchies. Delegation to less privileges processes is not safe + in cgroupsv1 (as a limitation of the kernel), hence systemd won't facilitate + access to it. + +3. Any BPF IP filter programs systemd installs will be installed with + `BPF_F_ALLOW_MULTI` so that your program can install additional ones. + +In unit files the `Delegate=` property is superficially exposed as +boolean. However, since v236 it optionally takes a list of controller names +instead. If so, delegation is requested for listed controllers +specifically. Note hat this only encodes a request. Depending on various +parameters it might happen that your service actually will get fewer +controllers delegated (for example, because the controller is not available on +the current kernel or was turned off) or more. If no list is specified +(i.e. the property simply set to `yes`) then all available controllers are +delegated. + +Let's stress one thing: delegation is available on scope and service units +only. It's expressly not available on slice units. Why that? Because slice +units are our *inner* nodes of the cgroup trees and we freely attach service +and scopes to them. If we'd allow delegation on slice units then this would +mean that that both systemd and your own manager would create/delete cgroups +below the slice unit and that conflicts with the single-writer rule. + +So, if you want to do your own raw cgroups kernel level access, then allocate a +scope unit, or a service unit (or just use the service unit you already have +for your service code), and turn on delegation for it. + +## Three Scenarios + +Let's say you write a container manager, and you wonder what to do regarding +cgroups for it, as you want your manager to be able to run on systemd systems. + +You basically have three options: + +1. 😊 The *integration-is-good* option. For this, you register each container you + have either as systemd service (i.e. let systemd invoke the executor binary + for you) or systemd scope (i.e. your manager executes the binary directly, + but then tells systemd about it. In this mode the administrator can use the + usual systemd resource management commands individually on containers. By + turning on `Delegate=` for these scopes or services you make it possible to + run cgroup-enabled programs in your containers, for example a systemd + instance running inside it. This option has two sub-options: + + a. You register the service or scope transiently directly by contacting + systemd via D-Bus. In this case systemd will just manage the unit for you and + nothing else. + + b. Instead you register the service or scope through `systemd-machined` + (also via D-Bus). This mini-daemon is basically just a proxy for the same + operations as in a. The main benefit of this: this way you let the system + know that what you are registering is a container, and this opens up + certain additional integration points. For example, `journalctl -M` can + then be used to directly look into any container's journal logs (should + the container run systemd inside), or `systemctl -M` can be used to + directly invoke systemd operations inside the containers. Moreover tools + like "ps" can then show you to which container a process belongs (`ps -eo + pid,comm,machine`), and even gnome-system-monitor supports it. + +2. 🙁 The *i-like-islands* option. If all you care about is your own cgroup tree, + and you want to have to do as little as possible with systemd and no + interest in integration with the rest of the system, then this is a valid + option. For this all you have to do is turn on `Delegate=` for your main + manager daemon. Then figure out the cgroup systemd placed your daemon in: + you can now freely create sub-cgroups beneath it. Don't forget the + *no-processes-in-inner-nodes* rule however: you have to move your main + daemon process out of that cgroup (and into a sub-cgroup) before you can + start further processes in any of your sub-cgroups. + +3. 🙁 The *i-like-continents* option. In this option you'd leave your manager + daemon where it is, and would not turn on delegation on its unit. However, + as first thing you register a new scope unit with systemd, and that scope + unit would have `Delegate=` turned on, and then you place all your + containers underneath it. From systemd's PoV there'd be two units: your + manager service and the big scope that contains all your containers in one. + +BTW: if for whatever reason you say "I hate D-Bus, I'll never call any D-Bus +API, kthxbye", then options #1 and #3 are not available, as they generally +involve talking to systemd from your program code, via D-Bus. You still have +option #2 in that case however, as you can simply set `Delegate=` in your +service's unit file and you are done and have your own sub-tree. In fact, #2 is +the one option that allows you to completely ignore systemd's existence: you +can entirely generically follow the single rule that you just use the cgroup +you are started in, and everything below it, whatever that might be. That said, +maybe if you dislike D-Bus and systemd that much, the better approach might be +to work on that, and widen your horizon a bit. You are welcome. + +## Controller Support + +systemd supports a number of controllers (but not all). Specifically, supported +are: + +* on cgroupsv1: `cpu`, `cpuacct`, `blkio`, `memory`, `devices`, `pids` +* on cgroupsv2: `cpu`, `io`, `memory`, `pids` + +It is our intention to natively support all cgroupsv2 controllers that might +come up sooner or later. However, regarding cgroupsv1: at this point we will +not add support for any other controllers anymore. This means systemd currently +does not and will never manage the following controllers on cgroupsv1: +`freezer`, `cpuset`, `net_cls`, `perf_event`, `net_prio`, `hugetlb`. Why not? +Depending on the case, either their API semantics or implementations aren't +really usable, or it's very clear they have no future on cgroupsv2, and we +won't add new code for stuff that clearly has no future. + +Effectively this means that all those mentioned cgroupsv1 controllers are up +for grabs: systemd won't manage them, and hence won't delegate them to your +code (however, systemd will still mount their hierarchies, simply because it +mounts all controller hierarchies it finds available in the kernel). If you +decide to use them, then that's fine, but systemd won't help you with it (but +also not interfere with it). To be nice to other tenants it might be wise to +replicate the cgroup hierarchies of the other controllers in them too however, +but of course that's between you and those other tenants, and systemd won't +care. Replicating the cgroup hierarchies in those unsupported controllers would +mean replicating the full cgroup paths in them, and hence the prefixing +`.slice` components too, otherwise the hierarchies will start being orthogonal +after all, and that's not really desirable. On more thing: systemd will clean +up after you in the hierarchies it manages: if your daemon goes down, its +cgroups will be removed too. You basically get the guarantee that you start +with a pristine cgroup sub-tree for your service or scope whenever it is +started. This is not the case however in the hierarchies systemd doesn't +manage. This means that your programs should be ready to deal with left-over +cgroups in them — from previous runs, and be extra careful with them as they +might still carry settings that might not be valid anymore. + +Note a particular asymmetry here: if your systemd version doesn't support a +specific controller on cgroupsv1 you can still make use of it for delegation, +by directly fiddling with its hierarchy and replicating the cgroup tree there +as necessary (as suggested above). However, on cgroupsv2 this is different: +separately mounted hierarchies are not available, and delegation has always to +happen through systemd itself. This means: when you update your kernel and it +adds a new, so far unseen controller, and you want to use it for delegation, +then you also need to update systemd to a version that groks it. + +## systemd as Container Payload + +systemd can happily run as a container payload's PID 1. Note that systemd +unconditionally needs write access to the cgroup tree however, hence you need +to delegate a sub-tree to it. Note that there's nothing too special you have to +do beyond that: just invoke systemd as PID 1 inside the root of the delegated +cgroup sub-tree, and it will figure out the rest: it will determine the cgroup +it is running in and take possession of it. It won't interfere with any cgroup +outside of the sub-tree it was invoked in. Use of `CLONE_NEWCGROUP` is hence +optional (but of course wise). + +Note one particular asymmetry here though: systemd will try to take possession +of the root cgroup you pass to it *in* *full*, i.e. it will not only +create/remove child cgroups below it it will also attempt to manage the +attributes of it. OTOH as mentioned above, when delegating a cgroup tree to +somebody else it only passes the rights to create/remove sub-cgroups, but will +insist on managing the delegated cgroup tree's top-level attributes. Or in +other words: systemd is *greedy* when accepting delegated cgroup trees and also +*greedy* when delegating them to others: it insists on managing attributes on +the specific cgroup in both cases. A container manager that is itself a payload +of a host systemd which wants to run a systemd as its own container payload +instead hence needs to insert an extra level in the hierarchy in between, so +that the systemd on the host and the one in the container won't fight for the +attributes. That said, you likely should do that anyway, due to the +no-processes-in-inner-cgroups rule, see below. + +When systemd runs as container payload it will make use of all hierarchies it +has write access to. For legacy mode you need to make at least +`/sys/fs/cgroup/systemd/` available, all other hierarchies are optional. For +hybrid mode you need to add `/sys/fs/cgroup/unified/`. Finally, for fully +unified you (of course, I guess) need to provide only `/sys/fs/cgroup/` itself. + +## Some Dos + +1. ⚡ If you go for implementation option 1a or 1b (as in the list above), then + each of your containers will have its own systemd-managed unit and hence + cgroup with possibly further sub-cgroups below. Typically the first process + running in that unit will be some kind of executor program, which will in + turn fork off the payload processes of the container. In this case don't + forget that there are two levels of delegation involved: first, systemd + delegates a group sub-tree to your executor. And then your executor should + delegate a sub-tree further down to the container payload. Oh, and because + of the no-process-in-inner-nodes rule, your executor needs to migrate itself + to a sub-cgroup of the cgroup it got delegated, too. Most likely you hence + want a two-pronged approach: below the cgroup you got started in, you want + one cgroup maybe called `supervisor/` where your manager runs in and then + for each container a sibling cgroup of that maybe called `payload-xyz/`. + +2. ⚡ Don't forget that the cgroups you create have to have names that are + suitable as UNIX file names, and that they live in the same namespace as the + various kernel attribute files. Hence, when you want to allow the user + arbitrary naming, you might need to escape some of the names (for example, + you really don't want to create a cgroup named `tasks`, just because the + user created a container by that name, because `tasks` after all is a magic + attribute in cgroupsv1, and your `mkdir()` will hence fail with `EEXIST`. In + systemd we do escaping by prefixing names that might collide with a kernel + attribute name with an underscore. You might want to do the same, but this + is really up to you how you do it. Just do it, and be careful. + +## Some Don'ts + +1. 🚫 Never create your own cgroups below arbitrary cgroups systemd manages, i.e + cgroups you haven't set `Delegate=` in. Specifically: 🔥 don't create your + own cgroups below the root cgroup 🔥. That's owned by systemd, and you will + step on systemd's toes if you ignore that, and systemd will step on + yours. Get your own delegated sub-tree, you may create as many cgroups there + as you like. Seriously, if you create cgroups directly in the cgroup root, + then all you do is ask for trouble. + +2. 🚫 Don't attempt to set `Delegate=` in slice units, and in particular not in + `-.slice`. It's not supported, and will generate an error. + +3. 🚫 Never *write* to any of the attributes of a cgroup systemd created for + you. It's systemd's private property. You are welcome to manipulate the + attributes of cgroups you created in your own delegated sub-tree, but the + cgroup tree of systemd itself is out of limits for you. It's fine to *read* + from any attribute you like however. That's totally OK and welcome. + +4. 🚫 When not using `CLONE_NEWCGROUP` when delegating a sub-tree to a container + payload running systemd, then don't get the idea that you can bind mount + only a sub-tree of the host's cgroup tree into the container. Part of the + cgroup API is that `/proc/$PID/cgroup` reports the cgroup path of every + process, and hence any path below `/sys/fs/cgroup/` needs to match what + `/proc/$PID/cgroup` of the payload processes reports. What you can do safely + however, is mount the upper parts of the cgroup tree read-only or even + replace it with an intermediary `tmpfs`, as long as the path to the + delegated sub-tree remains accessible as-is. + +5. ⚡ Think twice before delegating cgroupsv1 controllers to less privileged + containers. It's not safe, you basically allow your containers to freeze the + system with that and worse. Delegation is a strongpoint of cgroupsv2 though, + and there it's safe to treat delegation boundaries as privilege boundaries. + +And that's it for now. If you have further questions, refer to the systemd +mailing list. + +— Berlin, 2018-04-20 diff --git a/doc/UIDS-GIDS.md b/doc/UIDS-GIDS.md index e19cc88162..c380bbe140 100644 --- a/doc/UIDS-GIDS.md +++ b/doc/UIDS-GIDS.md @@ -241,3 +241,38 @@ really unused. It just means that these ranges have no well-established pre-defined purposes between Linux, generic low-level distributions and `systemd`. There might very well be other packages that allocate from these ranges. + +## Notes on resolvability of user and group names + +User names, UIDs, group names and GIDs don't have to be resolvable using NSS +(i.e. getpwuid() and getpwnam() and friends) all the time. However, systemd +makes the following requirements: + +System users generally have to be resolvable during early boot already. This +means they should not be provided by any networked service (as those usually +become available during late boot only), except if a local cache is kept that +makes them available during early boot too (i.e. before networking is +up). Specifically, system users need to be resolvable at least before +`systemd-udevd.service` and `systemd-tmpfiles.service` are started, as both +need to resolve system users — but note that there might be more services +requiring full resolvability of system users than just these two. + +Regular users do not need to be resolvable during early boot, it is sufficient +if they become resolvable during late boot. Specifically, regular users need to +be resolvable at the point in time the `nss-user-lookup.target` unit is +reached. This target unit is generally used as synchronization point between +providers of the user database and consumers of it. Services that require that +the user database is fully available (for example, the login service +`systemd-logind.service`) are ordered *after* it, while services that provide +parts of the user database (for example an LDAP user database client) are +ordered *before* it. Note that `nss-user-lookup.target` is a *passive* unit: in +order to minimize synchronization points on systems that don't need it the unit +is pulled into the initial transaction only if there's at least one service +that really needs it, and that means only if there's a service providing the +local user database somehow through IPC or suchlike. Or in other words: if you +hack on some networked user database project, then make sure you order your +service `Before=nss-user-lookup.target` and that you pull it in with +`Wants=nss-user-lookup.target`. However, if you hack on some project that needs +the user database to be up in full, then order your service +`After=nss-user-lookup.target`, but do *not* pull it in via a `Wants=` +dependency. diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb index e8e650cde6..532a1cc59c 100644 --- a/hwdb/60-evdev.hwdb +++ b/hwdb/60-evdev.hwdb @@ -461,6 +461,13 @@ evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:*svnTOSHIBA:pnTECRAM11* EVDEV_ABS_00=90:962:11 EVDEV_ABS_01=51:681:14 +# Toshiba Satellite R830 +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnTOSHIBA:pnSATELLITER830* + EVDEV_ABS_00=1238:5785:53 + EVDEV_ABS_01=1045:4826:76 + EVDEV_ABS_35=1238:5785:53 + EVDEV_ABS_36=1045:4826:76 + ######################################### # Razer ######################################### diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index 808ed54a77..2e24c86663 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -264,7 +264,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pn* KEYBOARD_KEY_85=brightnessdown # Fn+Down Brightness Down KEYBOARD_KEY_86=brightnessup # Fn+Up Brightness Up KEYBOARD_KEY_87=battery # Fn+F3 battery icon - KEYBOARD_KEY_88=unknown # Fn+F2 Turn On/Off Wireless - handled in hardware + KEYBOARD_KEY_88=!wlan # Fn+(F2|PrtScr|Home) Turn On/Off Wireless KEYBOARD_KEY_89=ejectclosecd # Fn+F10 Eject CD KEYBOARD_KEY_8a=suspend # Fn+F1 hibernate KEYBOARD_KEY_8b=switchvideomode # Fn+F8 CRT/LCD (high keycode: "displaytoggle") @@ -326,6 +326,11 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnLatitude*E7*:pvr* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnXPS*:pvr* KEYBOARD_KEY_8c=!unknown +# Dell XPS L702x +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDellInc.:pnDellSystemXPSL702X:pvr* + KEYBOARD_KEY_84=prog1 + KEYBOARD_KEY_85=prog2 + # Dell XPS12 9Q33 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnXPS12-9Q33*:pvr* KEYBOARD_KEY_88=wlan diff --git a/hwdb/60-sensor.hwdb b/hwdb/60-sensor.hwdb index 8385efd28d..d7204422bc 100644 --- a/hwdb/60-sensor.hwdb +++ b/hwdb/60-sensor.hwdb @@ -193,6 +193,12 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnJumper:pnEZpad:*:rvr.A006:* ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, 1 ######################################### +# Kazam +######################################### +sensor:modalias:acpi:KIOX000A*:dmi:bvnINSYDECorp.:bvrVISION.I22K*:svnKAZAM:pnVISION:* + ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1 + +######################################### # Lamina ######################################### sensor:modalias:acpi:SMO8500*:dmi:*svnLamina*:*pnT701BR.SE* diff --git a/man/file-hierarchy.xml b/man/file-hierarchy.xml index 3e28fc6bee..f14b9aa250 100644 --- a/man/file-hierarchy.xml +++ b/man/file-hierarchy.xml @@ -53,16 +53,16 @@ <title>Description</title> <para>Operating systems using the - <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> - system and service manager are organized based on a file system - hierarchy inspired by UNIX, more specifically the hierarchy - described in the <ulink - url="http://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.html">File - System Hierarchy</ulink> specification and - <citerefentry project='man-pages'><refentrytitle>hier</refentrytitle><manvolnum>7</manvolnum></citerefentry>. - This manual page describes a more minimal, modernized subset of - these specifications that defines more strictly the suggestions - and restrictions systemd makes on the file system + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> system and service + manager are organized based on a file system hierarchy inspired by UNIX, more specifically the hierarchy described + in the <ulink url="http://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.html">File System Hierarchy</ulink> + specification and <citerefentry + project='man-pages'><refentrytitle>hier</refentrytitle><manvolnum>7</manvolnum></citerefentry>, with various + extensions, partially documented in the <ulink + url="http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG Base Directory + Specification</ulink> and <ulink url="https://www.freedesktop.org/wiki/Software/xdg-user-dirs/">XDG User + Directories</ulink>. This manual page describes a more generalized, though minimal and modernized subset of these + specifications that defines more strictly the suggestions and restrictions systemd makes on the file system hierarchy.</para> <para>Many of the paths described here can be queried @@ -87,7 +87,7 @@ <term><filename>/boot</filename></term> <listitem><para>The boot partition used for bringing up the system. On EFI systems, this is possibly the EFI System - Partition, also see + Partition (ESP), also see <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>. This directory is usually strictly local to the host, and should be considered read-only, except when a new kernel or @@ -97,6 +97,15 @@ </varlistentry> <varlistentry> + <term><filename>/efi/</filename></term> + <listitem><para>If the boot partition <filename>/boot/</filename> is maintained separately from the EFI System + Partition (ESP), the latter is mounted here. Tools that need to operate on the EFI system partition should look + for it at this mount point first, and fall back to <filename>/boot/</filename> — if the former doesn't qualify + (for example if it is not a mount point or does not have the correct file system type + <constant>MSDOS_SUPER_MAGIC</constant>).</para></listitem> + </varlistentry> + + <varlistentry> <term><filename>/etc</filename></term> <listitem><para>System-specific configuration. This directory may or may not be read-only. Frequently, this directory is diff --git a/man/loginctl.xml b/man/loginctl.xml index 65c5227ed3..3863ec6621 100644 --- a/man/loginctl.xml +++ b/man/loginctl.xml @@ -98,11 +98,9 @@ <varlistentry> <term><option>--value</option></term> - <listitem> - <para>When printing properties with <command>show</command>, - only print the value, and skip the property name and - <literal>=</literal>.</para> - </listitem> + <listitem><para>When showing session/user/seat properties, + only print the value, and skip the property name and + <literal>=</literal>.</para></listitem> </varlistentry> <varlistentry> diff --git a/man/os-release.xml b/man/os-release.xml index 973e19747d..59db374411 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -74,7 +74,8 @@ shell style. All strings should be in UTF-8 format, and non-printable characters should not be used. It is not supported to concatenate multiple individually quoted strings. Lines - beginning with "#" shall be ignored as comments.</para> + beginning with "#" shall be ignored as comments. Blank lines are + permitted and ignored.</para> <para>The file <filename>/etc/os-release</filename> takes precedence over <filename>/usr/lib/os-release</filename>. diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index 68ebf02f2e..67b6c4fd32 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -98,8 +98,7 @@ <row> <entry><varname>kvm</varname></entry> - <entry>Linux KVM kernel virtual machine, with whatever software, except - Oracle Virtualbox</entry> + <entry>Linux KVM kernel virtual machine, with whatever software, except Oracle Virtualbox</entry> </row> <row> @@ -119,8 +118,7 @@ <row> <entry><varname>oracle</varname></entry> - <entry>Oracle VM VirtualBox (historically marketed by innotek and Sun Microsystems), - for legacy and KVM hypervisor</entry> + <entry>Oracle VM VirtualBox (historically marketed by innotek and Sun Microsystems), for legacy and KVM hypervisor</entry> </row> <row> diff --git a/man/systemd-escape.xml b/man/systemd-escape.xml index 513e9a6a97..98cedef77d 100644 --- a/man/systemd-escape.xml +++ b/man/systemd-escape.xml @@ -122,6 +122,7 @@ <varlistentry> <term><option>--unescape</option></term> + <term><option>-u</option></term> <listitem><para>Instead of escaping the specified strings, undo the escaping, reversing the operation. May not be used in @@ -132,6 +133,7 @@ <varlistentry> <term><option>--mangle</option></term> + <term><option>-m</option></term> <listitem><para>Like <option>--escape</option>, but only escape characters that are obviously not escaped yet, and diff --git a/man/systemd-journal-upload.xml b/man/systemd-journal-upload.xml index a43062a6d5..fecffd5d9b 100644 --- a/man/systemd-journal-upload.xml +++ b/man/systemd-journal-upload.xml @@ -268,10 +268,10 @@ echo 0001 >serial SERVER=server CLIENT=client -openssl req -newkey rsa:1024 -nodes -out $SERVER.csr -keyout $SERVER.key -subj "/CN=$SERVER/" +openssl req -newkey rsa:2048 -nodes -out $SERVER.csr -keyout $SERVER.key -subj "/CN=$SERVER/" openssl ca -batch -config ca.conf -notext -in $SERVER.csr -out $SERVER.pem -openssl req -newkey rsa:1024 -nodes -out $CLIENT.csr -keyout $CLIENT.key -subj "/CN=$CLIENT/" +openssl req -newkey rsa:2048 -nodes -out $CLIENT.csr -keyout $CLIENT.key -subj "/CN=$CLIENT/" openssl ca -batch -config ca.conf -notext -in $CLIENT.csr -out $CLIENT.pem </programlisting> diff --git a/man/systemd-networkd-wait-online.service.xml b/man/systemd-networkd-wait-online.service.xml index 96715cad2f..7cec7da9d7 100644 --- a/man/systemd-networkd-wait-online.service.xml +++ b/man/systemd-networkd-wait-online.service.xml @@ -26,7 +26,7 @@ <refentry id="systemd-networkd-wait-online.service" conditional='ENABLE_NETWORKD'> <refentryinfo> - <title>systemd-networkd.service</title> + <title>systemd-networkd-wait-online.service</title> <productname>systemd</productname> <authorgroup> diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index c1867b4ed2..f1bbfb94a2 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1987,7 +1987,9 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy <listitem><para>Variables read from files specified via <varname>EnvironmentFile=</varname> in the unit file</para></listitem> - <listitem><para>Variables set by any PAM modules in case <varname>PAMName=</varname> is in effect, cf. <citerefentry project='man-pages'><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry></para></listitem> + <listitem><para>Variables set by any PAM modules in case <varname>PAMName=</varname> is in effect, + cf. <citerefentry + project='man-pages'><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry></para></listitem> </itemizedlist> <para>If the same environment variables are set by multiple of these sources, the later source — according to the @@ -1995,7 +1997,8 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy <varname>UnsetEnvironment=</varname> are removed again from the compiled environment variable list, immediately before it is passed to the executed process.</para> - <para>The following select environment variables are set by the service manager itself for each invoked process:</para> + <para>The following select environment variables are set or propagated by the service manager for each invoked + process:</para> <variablelist class='environment-variables'> <varlistentry> @@ -2046,24 +2049,11 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy <varlistentry> <term><varname>$XDG_RUNTIME_DIR</varname></term> - <listitem><para>The directory for volatile state. Set for the - user <command>systemd</command> instance, and also in user - sessions. See - <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>. - </para></listitem> - </varlistentry> - - <varlistentry> - <term><varname>$XDG_SESSION_ID</varname></term> - <term><varname>$XDG_SEAT</varname></term> - <term><varname>$XDG_VTNR</varname></term> - - <listitem><para>The identifier of the session, the seat name, - and virtual terminal of the session. Set by - <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry> - for login sessions. <varname>$XDG_SEAT</varname> and - <varname>$XDG_VTNR</varname> will only be set when attached to - a seat and a tty.</para></listitem> + <listitem><para>The directory to use for runtime objects (such as IPC objects) and volatile state. Set for all + services run by the user <command>systemd</command> instance, as well as any system services that use + <varname>PAMName=</varname> with a PAM stack that includes <command>pam_systemd</command>. See below and + <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry> for more + information.</para></listitem> </varlistentry> <varlistentry> @@ -2317,6 +2307,11 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy </listitem> </varlistentry> </variablelist> + + <para>For system services, when <varname>PAMName=</varname> is enabled and <command>pam_systemd</command> is part + of the selected PAM stack, additional environment variables defined by systemd may be set for + services. Specifically, these are <varname>$XDG_SEAT</varname>, <varname>$XDG_VTNR</varname>, see + <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry> for details.</para> </refsect1> <refsect1> diff --git a/man/systemd.special.xml b/man/systemd.special.xml index 4f29a24c27..5bb279333c 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -458,6 +458,10 @@ functionality to other hosts generally do not need to pull this in.</para> + <para>systemd automatically adds dependencies of type <varname>Wants=</varname> and <varname>After=</varname> + for this target unit to all SysV init script service units with an LSB header referring to the + <literal>$network</literal> facility.</para> + <para>Note that this unit is only useful during the original system start-up logic. After the system has completed booting up, it will not track the online state of the system anymore. Due to this it cannot be used as a network connection monitor concept, it is purely a one-time system start-up concept.</para> @@ -849,11 +853,6 @@ information. Also see <filename>network-online.target</filename> described above.</para> - - <para>systemd automatically adds dependencies of type - <varname>After=</varname> for this target unit to all SysV - init script service units with an LSB header referring to - the <literal>$network</literal> facility.</para> </listitem> </varlistentry> <varlistentry> @@ -869,31 +868,24 @@ <varlistentry> <term><filename>nss-lookup.target</filename></term> <listitem> - <para>A target that should be used as synchronization point - for all host/network name service lookups. Note that this is - independent of user/group name lookups for which - <filename>nss-user-lookup.target</filename> should be used. - All services for which the availability of full host/network - name resolution is essential should be ordered after this - target, but not pull it in. systemd automatically adds - dependencies of type <varname>After=</varname> for this - target unit to all SysV init script service units with an - LSB header referring to the <literal>$named</literal> - facility.</para> + <para>A target that should be used as synchronization point for all host/network name service lookups. Note + that this is independent of UNIX user/group name lookups for which <filename>nss-user-lookup.target</filename> + should be used. All services for which the availability of full host/network name resolution is essential + should be ordered after this target, but not pull it in. systemd automatically adds dependencies of type + <varname>After=</varname> for this target unit to all SysV init script service units with an LSB header + referring to the <literal>$named</literal> facility.</para> </listitem> </varlistentry> <varlistentry> <term><filename>nss-user-lookup.target</filename></term> <listitem> - <para>A target that should be used as synchronization point - for all user/group name service lookups. Note that this is - independent of host/network name lookups for which - <filename>nss-lookup.target</filename> should be used. All - services for which the availability of the full user/group - database is essential should be ordered after this target, - but not pull it in. Note that system users are always - resolvable, and hence do not require any special ordering - against this target.</para> + <para>A target that should be used as synchronization point for all regular UNIX user/group name service + lookups. Note that this is independent of host/network name lookups for which + <filename>nss-lookup.target</filename> should be used. All services for which the availability of the full + user/group database is essential should be ordered after this target, but not pull it in. All services which + provide parts of the user/group database should be ordered before this target, and pull it in. Note that this + unit is only relevant for regular users and groups — system users and groups are required to be resolvable + during earliest boot already, and hence do not need any special ordering against this target.</para> </listitem> </varlistentry> <varlistentry> diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 8365cddc42..0c11119707 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1555,28 +1555,28 @@ </row> <row> <entry><literal>%N</literal></entry> - <entry>Unescaped full unit name</entry> - <entry>Same as <literal>%n</literal>, but with escaping undone. This undoes the escaping used when generating unit names from arbitrary strings (see above). </entry> + <entry>Full unit name</entry> + <entry>Same as <literal>%n</literal>, but with the type suffix removed.</entry> </row> <row> <entry><literal>%p</literal></entry> <entry>Prefix name</entry> - <entry>For instantiated units, this refers to the string before the <literal>@</literal> character of the unit name. For non-instantiated units, this refers to the name of the unit with the type suffix removed.</entry> + <entry>For instantiated units, this refers to the string before the first <literal>@</literal> character of the unit name. For non-instantiated units, same as <literal>%N</literal>.</entry> </row> <row> <entry><literal>%P</literal></entry> <entry>Unescaped prefix name</entry> - <entry>Same as <literal>%p</literal>, but with escaping undone</entry> + <entry>Same as <literal>%p</literal>, but with escaping undone.</entry> </row> <row> <entry><literal>%i</literal></entry> <entry>Instance name</entry> - <entry>For instantiated units: this is the string between the <literal>@</literal> character and the suffix of the unit name.</entry> + <entry>For instantiated units this is the string between the first <literal>@</literal> character and the type suffix. Empty for non-instantiated units.</entry> </row> <row> <entry><literal>%I</literal></entry> <entry>Unescaped instance name</entry> - <entry>Same as <literal>%i</literal>, but with escaping undone</entry> + <entry>Same as <literal>%i</literal>, but with escaping undone.</entry> </row> <row> <entry><literal>%f</literal></entry> diff --git a/man/udevadm.xml b/man/udevadm.xml index 1495f556f3..6bb54c86d8 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -131,9 +131,9 @@ <filename><optional>/sys</optional>/class/block/sda</filename>. Note that this option usually is not very useful, since <command>udev</command> can guess the type of the - argument, so <command>udevadm - --devpath=/class/block/sda</command> is equivalent to - <command>udevadm /sys/class/block/sda</command>.</para> + argument, so <command>udevadm info + --path=/class/block/sda</command> is equivalent to + <command>udevadm info /sys/class/block/sda</command>.</para> </listitem> </varlistentry> <varlistentry> @@ -144,8 +144,8 @@ e.g. <filename><optional>/dev</optional>/sda</filename>. Note that this option usually is not very useful, since <command>udev</command> can guess the type of the - argument, so <command>udevadm --name=sda</command> is - equivalent to <command>udevadm /dev/sda</command>.</para> + argument, so <command>udevadm info --name=sda</command> is + equivalent to <command>udevadm info /dev/sda</command>.</para> </listitem> </varlistentry> <varlistentry> diff --git a/meson.build b/meson.build index 358b41b9d1..a2dc4040df 100644 --- a/meson.build +++ b/meson.build @@ -387,8 +387,7 @@ if cc.get_id() == 'clang' foreach arg : ['-Wno-typedef-redefinition', '-Wno-gnu-variable-sized-type-not-at-end', ] - if cc.has_argument(arg, - name : '@0@ is supported'.format(arg)) + if cc.has_argument(arg) add_project_arguments(arg, language : 'c') endif endforeach @@ -429,8 +428,7 @@ endforeach if get_option('buildtype') != 'debug' foreach arg : ['-ffunction-sections', '-fdata-sections'] - if cc.has_argument(arg, - name : '@0@ is supported'.format(arg)) + if cc.has_argument(arg) add_project_arguments(arg, language : 'c') endif endforeach diff --git a/src/activate/activate.c b/src/activate/activate.c index c856c8c100..e605d50ce0 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -201,7 +201,7 @@ static int exec_process(const char* name, char **argv, char **env, int start_fd, r = rearrange_stdio(start_fd, start_fd, STDERR_FILENO); /* invalidates start_fd on success + error */ if (r < 0) - return log_error_errno(errno, "Failed to move fd to stdin+stdout: %m"); + return log_error_errno(r, "Failed to move fd to stdin+stdout: %m"); } else { if (start_fd != SD_LISTEN_FDS_START) { diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c index 6a93c9109c..1e897cb982 100644 --- a/src/basic/audit-util.c +++ b/src/basic/audit-util.c @@ -95,10 +95,9 @@ bool use_audit(void) { fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); if (fd < 0) { cached_use = !IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT, EPERM); - if (errno == EPERM) - log_debug_errno(errno, "Audit access prohibited, won't talk to audit"); - } - else { + if (!cached_use) + log_debug_errno(errno, "Won't talk to audit: %m"); + } else { cached_use = true; safe_close(fd); } diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 3d30497f74..0db8292e70 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -1703,7 +1703,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) { /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */ if (mkdir(new_path, 0755) < 0) - return r; + return -errno; plain_directory = true; } else if (r < 0) diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index c0962f288f..296da2898b 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -2598,8 +2598,10 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { } r = write_string_stream(f, s, 0); - if (r < 0) + if (r < 0) { log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs); + clearerr(f); + } } } diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 678ab12bb8..6d7875361c 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -361,14 +361,21 @@ bool fdname_is_valid(const char *s) { } int fd_get_path(int fd, char **ret) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int dir = -1; + char fdname[DECIMAL_STR_MAX(int)]; int r; - xsprintf(procfs_path, "/proc/self/fd/%i", fd); + dir = open("/proc/self/fd/", O_CLOEXEC | O_DIRECTORY | O_PATH); + if (dir < 0) + /* /proc is not available or not set up properly, we're most likely + * in some chroot environment. */ + return errno == ENOENT ? -EOPNOTSUPP : -errno; - r = readlink_malloc(procfs_path, ret); + xsprintf(fdname, "%i", fd); - if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */ + r = readlinkat_malloc(dir, fdname, ret); + if (r == -ENOENT) + /* If the file doesn't exist the fd is invalid */ return -EBADF; return r; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 29b941348a..9875abff51 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -63,6 +63,7 @@ int write_string_stream_ts( struct timespec *ts) { bool needs_nl; + int r; assert(f); assert(line); @@ -87,6 +88,13 @@ int write_string_stream_ts( if (fputc('\n', f) == EOF) return -errno; + if (flags & WRITE_STRING_FILE_SYNC) + r = fflush_sync_and_check(f); + else + r = fflush_and_check(f); + if (r < 0) + return r; + if (ts) { struct timespec twice[2] = {*ts, *ts}; @@ -94,10 +102,7 @@ int write_string_stream_ts( return -errno; } - if (flags & WRITE_STRING_FILE_SYNC) - return fflush_sync_and_check(f); - else - return fflush_and_check(f); + return 0; } static int write_string_file_atomic( diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 85c8070a1b..c65ba4bfe5 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -978,8 +978,19 @@ int fsync_directory_of_file(int fd) { return r; r = fd_get_path(fd, &path); - if (r < 0) + if (r < 0) { + log_debug("Failed to query /proc/self/fd/%d%s: %m", + fd, + r == -EOPNOTSUPP ? ", ignoring" : ""); + + if (r == -EOPNOTSUPP) + /* If /proc is not available, we're most likely running in some + * chroot environment, and syncing the directory is not very + * important in that case. Let's just silently do nothing. */ + return 0; + return r; + } if (!path_is_absolute(path)) return -EINVAL; diff --git a/src/basic/log.c b/src/basic/log.c index 16a2431c54..9a39da7c16 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -699,9 +699,8 @@ int log_internalv_realm( if (_likely_(LOG_PRI(level) > log_max_level[realm])) return -error; - /* Make sure that %m maps to the specified error */ - if (error != 0) - errno = error; + /* Make sure that %m maps to the specified error (or "Success"). */ + errno = error; (void) vsnprintf(buffer, sizeof buffer, format, ap); @@ -749,9 +748,8 @@ static int log_object_internalv( if (_likely_(LOG_PRI(level) > log_max_level[LOG_REALM_SYSTEMD])) return -error; - /* Make sure that %m maps to the specified error */ - if (error != 0) - errno = error; + /* Make sure that %m maps to the specified error (or "Success"). */ + errno = error; /* Prepend the object name before the message */ if (object) { @@ -874,8 +872,7 @@ int log_format_iovec( * since vasprintf() leaves it afterwards at * an undefined location */ - if (error != 0) - errno = error; + errno = error; va_copy(aq, ap); r = vasprintf(&m, format, aq); @@ -976,8 +973,7 @@ int log_struct_internal( while (format) { va_list aq; - if (error != 0) - errno = error; + errno = error; va_copy(aq, ap); (void) vsnprintf(buf, sizeof buf, format, aq); @@ -1275,8 +1271,7 @@ int log_syntax_internal( if (log_target == LOG_TARGET_NULL) return -error; - if (error != 0) - errno = error; + errno = error; va_start(ap, format); (void) vsnprintf(buffer, sizeof buffer, format, ap); diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h index 34b8956a12..86422ced72 100644 --- a/src/basic/missing_syscall.h +++ b/src/basic/missing_syscall.h @@ -416,8 +416,14 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) { #if !HAVE_STATX # ifndef __NR_statx -# if defined __i386__ +# if defined __aarch64__ || defined __arm__ +# define __NR_statx 397 +# elif defined __alpha__ +# define __NR_statx 522 +# elif defined __i386__ || defined __powerpc64__ # define __NR_statx 383 +# elif defined __sparc__ +# define __NR_statx 360 # elif defined __x86_64__ # define __NR_statx 332 # else diff --git a/src/basic/virt.c b/src/basic/virt.c index 39b4a98d89..971701acca 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -332,13 +332,16 @@ int detect_vm(void) { /* We have to use the correct order here: * - * -> First try to detect Oracle Virtualbox, even if it uses KVM. - * -> Second try to detect from cpuid, this will report KVM for - * whatever software is used even if info in dmi is overwritten. - * -> Third try to detect from dmi. */ + * → First, try to detect Oracle Virtualbox, even if it uses KVM, as well as Xen even if it cloaks as Microsoft + * Hyper-V. + * + * → Second, try to detect from CPUID, this will report KVM for whatever software is used even if info in DMI is + * overwritten. + * + * → Third, try to detect from DMI. */ dmi = detect_vm_dmi(); - if (dmi == VIRTUALIZATION_ORACLE) { + if (IN_SET(dmi, VIRTUALIZATION_ORACLE, VIRTUALIZATION_XEN)) { r = dmi; goto finish; } diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 992a3ba4c2..2751b8da4f 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -50,12 +50,17 @@ stub_sources = ''' if conf.get('ENABLE_EFI') == 1 and get_option('gnu-efi') != 'false' efi_cc = get_option('efi-cc') efi_ld = get_option('efi-ld') - efi_incdir = get_option('efi-includedir') - have_header = (gnu_efi_arch != '' and - cc.has_header('@0@/@1@/efibind.h'.format(efi_incdir, gnu_efi_arch))) - if have_header and EFI_MACHINE_TYPE_NAME == '' + gnu_efi_path_arch = '' + foreach name : [gnu_efi_arch, EFI_MACHINE_TYPE_NAME] + if (gnu_efi_path_arch == '' and name != '' and + cc.has_header('@0@/@1@/efibind.h'.format(efi_incdir, name))) + gnu_efi_path_arch = name + endif + endforeach + + if gnu_efi_path_arch != '' and EFI_MACHINE_TYPE_NAME == '' error('gnu-efi is available, but EFI_MACHINE_TYPE_NAME is unknown') endif @@ -68,7 +73,7 @@ if conf.get('ENABLE_EFI') == 1 and get_option('gnu-efi') != 'false' endif endif - have_gnu_efi = have_header and efi_libdir != '' + have_gnu_efi = gnu_efi_path_arch != '' and efi_libdir != '' else have_gnu_efi = false endif @@ -91,7 +96,7 @@ if have_gnu_efi objcopy = find_program('objcopy') efi_ldsdir = get_option('efi-ldsdir') - arch_lds = 'elf_@0@_efi.lds'.format(gnu_efi_arch) + arch_lds = 'elf_@0@_efi.lds'.format(gnu_efi_path_arch) if efi_ldsdir == '' efi_ldsdir = join_paths(efi_libdir, 'gnuefi') cmd = run_command('test', '-f', join_paths(efi_ldsdir, arch_lds)) @@ -121,7 +126,7 @@ if have_gnu_efi '-Wsign-compare', '-Wno-missing-field-initializers', '-isystem', efi_incdir, - '-isystem', join_paths(efi_incdir, gnu_efi_arch), + '-isystem', join_paths(efi_incdir, gnu_efi_path_arch), '-include', efi_config_h] if efi_arch == 'x86_64' compile_args += ['-mno-red-zone', @@ -141,7 +146,7 @@ if have_gnu_efi '-nostdlib', '-znocombreloc', '-L', efi_libdir, - join_paths(efi_ldsdir, 'crt0-efi-@0@.o'.format(gnu_efi_arch))] + join_paths(efi_ldsdir, 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))] if efi_arch == 'aarch64' or efi_arch == 'arm' # Aarch64 and ARM32 don't have an EFI capable objcopy. Use 'binary' # instead, and add required symbols manually. diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c index 48666f64a2..deebafbf47 100644 --- a/src/core/bpf-firewall.c +++ b/src/core/bpf-firewall.c @@ -705,13 +705,14 @@ int bpf_firewall_supported(void) { 1, BPF_F_NO_PREALLOC); if (fd < 0) { - log_debug_errno(r, "Can't allocate BPF LPM TRIE map, BPF firewalling is not supported: %m"); + log_debug_errno(fd, "Can't allocate BPF LPM TRIE map, BPF firewalling is not supported: %m"); return supported = BPF_FIREWALL_UNSUPPORTED; } safe_close(fd); - if (bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &program) < 0) { + r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &program); + if (r < 0) { log_debug_errno(r, "Can't allocate CGROUP SKB BPF program, BPF firewalling is not supported: %m"); return supported = BPF_FIREWALL_UNSUPPORTED; } diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index f480664613..489112087f 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -32,6 +32,7 @@ #include "fd-util.h" #include "fileio.h" #include "path-util.h" +#include "unit.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy); @@ -351,13 +352,13 @@ static int bus_cgroup_set_transient_property( if (streq(name, "Delegate")) { int b; - if (!UNIT_VTABLE(u)->can_delegate) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type"); - r = sd_bus_message_read(message, "b", &b); if (r < 0) return r; + if (!UNIT_VTABLE(u)->can_delegate && b) + log_unit_notice(u, "Delegate=yes set, but has no effect for unit type"); + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { c->delegate = b; c->delegate_controllers = b ? _CGROUP_MASK_ALL : 0; @@ -370,9 +371,6 @@ static int bus_cgroup_set_transient_property( } else if (streq(name, "DelegateControllers")) { CGroupMask mask = 0; - if (!UNIT_VTABLE(u)->can_delegate) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type"); - r = sd_bus_message_enter_container(message, 'a', "s"); if (r < 0) return r; @@ -389,7 +387,7 @@ static int bus_cgroup_set_transient_property( cc = cgroup_controller_from_string(t); if (cc < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown cgroup contoller '%s'", t); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown cgroup controller '%s'", t); mask |= CGROUP_CONTROLLER_TO_MASK(cc); } @@ -414,6 +412,9 @@ static int bus_cgroup_set_transient_property( unit_write_settingf(u, flags, name, "Delegate=%s", strempty(t)); } + if (!UNIT_VTABLE(u)->can_delegate && c->delegate) + log_unit_notice(u, "Delegate=yes set, but has no effect for unit type"); + return 1; } diff --git a/src/core/device.c b/src/core/device.c index b0dd469fd1..a57c34ddf1 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -769,8 +769,8 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, if (revents != EPOLLIN) { static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5); - if (!ratelimit_test(&limit)) - log_error_errno(errno, "Failed to get udev event: %m"); + if (ratelimit_test(&limit)) + log_warning("Failed to get udev event"); if (!(revents & EPOLLIN)) return 0; } diff --git a/src/core/execute.c b/src/core/execute.c index 7292b815db..2f4b70ab3b 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2311,14 +2311,7 @@ static int apply_mount_namespace( _cleanup_strv_free_ char **empty_directories = NULL; char *tmp = NULL, *var = NULL; const char *root_dir = NULL, *root_image = NULL; - NamespaceInfo ns_info = { - .ignore_protect_paths = false, - .private_dev = context->private_devices, - .protect_control_groups = context->protect_control_groups, - .protect_kernel_tunables = context->protect_kernel_tunables, - .protect_kernel_modules = context->protect_kernel_modules, - .mount_apivfs = context->mount_apivfs, - }; + NamespaceInfo ns_info = {}; bool needs_sandboxing; BindMount *bind_mounts = NULL; unsigned n_bind_mounts = 0; @@ -2358,6 +2351,16 @@ static int apply_mount_namespace( needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & EXEC_COMMAND_FULLY_PRIVILEGED); + if (needs_sandboxing) + ns_info = (NamespaceInfo) { + .ignore_protect_paths = false, + .private_dev = context->private_devices, + .protect_control_groups = context->protect_control_groups, + .protect_kernel_tunables = context->protect_kernel_tunables, + .protect_kernel_modules = context->protect_kernel_modules, + .mount_apivfs = context->mount_apivfs, + }; + r = setup_namespace(root_dir, root_image, &ns_info, context->read_write_paths, needs_sandboxing ? context->read_only_paths : NULL, diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 9c27972aff..1ecf5c0d9f 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -22,6 +22,7 @@ #include <ftw.h> #include <stdlib.h> #include <sys/mount.h> +#include <sys/statvfs.h> #include <unistd.h> #include "alloc-util.h" @@ -387,6 +388,35 @@ static int nftw_cb( return FTW_CONTINUE; }; + +static int relabel_cgroup_filesystems(void) { + int r; + struct statfs st; + + r = cg_all_unified(); + if (r == 0) { + /* Temporarily remount the root cgroup filesystem to give it a proper label. Do this + only when the filesystem has been already populated by a previous instance of systemd + running from initrd. Otherwise don't remount anything and leave the filesystem read-write + for the cgroup filesystems to be mounted inside. */ + r = statfs("/sys/fs/cgroup", &st); + if (r < 0) { + return log_error_errno(errno, "Failed to determine mount flags for /sys/fs/cgroup: %m"); + } + + if (st.f_flags & ST_RDONLY) + (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT, NULL); + + label_fix("/sys/fs/cgroup", false, false); + nftw("/sys/fs/cgroup", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + + if (st.f_flags & ST_RDONLY) + (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT|MS_RDONLY, NULL); + } else if (r < 0) + return log_error_errno(r, "Failed to determine whether we are in all unified mode: %m"); + + return 0; +} #endif int mount_setup(bool loaded_policy) { @@ -411,15 +441,9 @@ int mount_setup(bool loaded_policy) { nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - /* Temporarily remount the root cgroup filesystem to give it a proper label. */ - r = cg_all_unified(); - if (r == 0) { - (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT, NULL); - label_fix("/sys/fs/cgroup", false, false); - nftw("/sys/fs/cgroup", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT|MS_RDONLY, NULL); - } else if (r < 0) - return log_error_errno(r, "Failed to determine whether we are in all unified mode: %m"); + r = relabel_cgroup_filesystems(); + if (r < 0) + return r; after_relabel = now(CLOCK_MONOTONIC); diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 58c9a9de79..fec9c276fc 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -269,11 +269,11 @@ static void sync_with_progress(void) { int main(int argc, char *argv[]) { bool need_umount, need_swapoff, need_loop_detach, need_dm_detach; - bool in_container, use_watchdog = false; + bool in_container, use_watchdog = false, can_initrd; _cleanup_free_ char *cgroup = NULL; char *arguments[3]; unsigned retries; - int cmd, r; + int cmd, r, umount_log_level = LOG_INFO; static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL}; char *watchdog_device; @@ -349,6 +349,7 @@ int main(int argc, char *argv[]) { need_swapoff = !in_container; need_loop_detach = !in_container; need_dm_detach = !in_container; + can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0; /* Unmount all mountpoints, swaps, and loopback devices */ for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) { @@ -366,7 +367,7 @@ int main(int argc, char *argv[]) { if (need_umount) { log_info("Unmounting file systems."); - r = umount_all(&changed); + r = umount_all(&changed, umount_log_level); if (r == 0) { need_umount = false; log_info("All filesystems unmounted."); @@ -390,7 +391,7 @@ int main(int argc, char *argv[]) { if (need_loop_detach) { log_info("Detaching loop devices."); - r = loopback_detach_all(&changed); + r = loopback_detach_all(&changed, umount_log_level); if (r == 0) { need_loop_detach = false; log_info("All loop devices detached."); @@ -402,7 +403,7 @@ int main(int argc, char *argv[]) { if (need_dm_detach) { log_info("Detaching DM devices."); - r = dm_detach_all(&changed); + r = dm_detach_all(&changed, umount_log_level); if (r == 0) { need_dm_detach = false; log_info("All DM devices detached."); @@ -419,6 +420,16 @@ int main(int argc, char *argv[]) { goto initrd_jump; } + if (!changed && umount_log_level == LOG_INFO && !can_initrd) { + /* There are things we cannot get rid of. Loop one more time + * with LOG_ERR to inform the user. Note that we don't need + * to do this if there is a initrd to switch to, because that + * one is likely to get rid of the remounting mounts. If not, + * it will log about them. */ + umount_log_level = LOG_ERR; + continue; + } + /* If in this iteration we didn't manage to * unmount/deactivate anything, we simply give up */ if (!changed) { @@ -450,8 +461,7 @@ int main(int argc, char *argv[]) { arguments[2] = NULL; execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments); - if (!in_container && !in_initrd() && - access("/run/initramfs/shutdown", X_OK) == 0) { + if (can_initrd) { r = switch_root_initramfs(); if (r >= 0) { argv[0] = (char*) "/shutdown"; diff --git a/src/core/umount.c b/src/core/umount.c index de826edd4d..ab2d4ffa62 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -48,8 +48,9 @@ typedef struct MountPoint { char *path; - char *options; - char *type; + char *remount_options; + unsigned long remount_flags; + bool try_remount_ro; dev_t devnum; LIST_FIELDS(struct MountPoint, mount_point); } MountPoint; @@ -61,8 +62,7 @@ static void mount_point_free(MountPoint **head, MountPoint *m) { LIST_REMOVE(mount_point, *head, m); free(m->path); - free(m->options); - free(m->type); + free(m->remount_options); free(m); } @@ -85,8 +85,7 @@ static int mount_points_list_get(MountPoint **head) { return -errno; for (i = 1;; i++) { - _cleanup_free_ char *path = NULL, *options = NULL, *type = NULL; - char *p = NULL; + _cleanup_free_ char *path = NULL, *options = NULL, *flags = NULL, *type = NULL, *p = NULL; MountPoint *m; int k; @@ -96,15 +95,15 @@ static int mount_points_list_get(MountPoint **head) { "%*s " /* (3) major:minor */ "%*s " /* (4) root */ "%ms " /* (5) mount point */ - "%*s" /* (6) mount flags */ + "%ms" /* (6) mount flags */ "%*[^-]" /* (7) optional fields */ "- " /* (8) separator */ "%ms " /* (9) file system type */ "%*s" /* (10) mount source */ "%ms" /* (11) mount options */ "%*[^\n]", /* some rubbish at the end */ - &path, &type, &options); - if (k != 3) { + &path, &flags, &type, &options); + if (k != 4) { if (k == EOF) break; @@ -127,22 +126,53 @@ static int mount_points_list_get(MountPoint **head) { mount_point_ignore(p) || path_startswith(p, "/dev") || path_startswith(p, "/sys") || - path_startswith(p, "/proc")) { - free(p); + path_startswith(p, "/proc")) continue; - } m = new0(MountPoint, 1); - if (!m) { - free(p); + if (!m) return -ENOMEM; - } - m->path = p; - m->options = options; - options = NULL; - m->type = type; - type = NULL; + free_and_replace(m->path, p); + + /* If we are in a container, don't attempt to + * read-only mount anything as that brings no real + * benefits, but might confuse the host, as we remount + * the superblock here, not the bind mount. + * + * If the filesystem is a network fs, also skip the + * remount. It brings no value (we cannot leave + * a "dirty fs") and could hang if the network is down. + * Note that umount2() is more careful and will not + * hang because of the network being down. */ + m->try_remount_ro = detect_container() <= 0 && + !fstype_is_network(type) && + !fstype_is_api_vfs(type) && + !fstype_is_ro(type) && + !fstab_test_yes_no_option(options, "ro\0rw\0"); + + if (m->try_remount_ro) { + _cleanup_free_ char *unknown_flags = NULL; + + /* mount(2) states that mount flags and options need to be exactly the same + * as they were when the filesystem was mounted, except for the desired + * changes. So we reconstruct both here and adjust them for the later + * remount call too. */ + + r = mount_option_mangle(flags, 0, &m->remount_flags, &unknown_flags); + if (r < 0) + return r; + if (!isempty(unknown_flags)) + log_warning("Ignoring unknown mount flags '%s'.", unknown_flags); + + r = mount_option_mangle(options, m->remount_flags, &m->remount_flags, &m->remount_options); + if (r < 0) + return r; + + /* MS_BIND is special. If it is provided it will only make the mount-point + * read-only. If left out, the super block itself is remounted, which we want. */ + m->remount_flags = (m->remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND; + } LIST_PREPEND(mount_point, *head, m); } @@ -333,6 +363,8 @@ static int delete_loopback(const char *device) { _cleanup_close_ int fd = -1; int r; + assert(device); + fd = open(device, O_RDONLY|O_CLOEXEC); if (fd < 0) return errno == ENOENT ? 0 : -errno; @@ -382,12 +414,14 @@ static bool nonunmountable_path(const char *path) { || path_startswith(path, "/run/initramfs"); } -static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) { +static int remount_with_timeout(MountPoint *m, int umount_log_level) { pid_t pid; int r; BLOCK_SIGNALS(SIGCHLD); + assert(m); + /* Due to the possiblity of a remount operation hanging, we * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular remount @@ -396,12 +430,12 @@ static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) { if (r < 0) return r; if (r == 0) { - log_info("Remounting '%s' read-only in with options '%s'.", m->path, options); + log_info("Remounting '%s' read-only in with options '%s'.", m->path, m->remount_options); /* Start the mount operation here in the child */ - r = mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, options); + r = mount(NULL, m->path, NULL, m->remount_flags, m->remount_options); if (r < 0) - log_error_errno(errno, "Failed to remount '%s' read-only: %m", m->path); + log_full_errno(umount_log_level, errno, "Failed to remount '%s' read-only: %m", m->path); _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } @@ -411,19 +445,21 @@ static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) { log_error_errno(r, "Remounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid); (void) kill(pid, SIGKILL); } else if (r == -EPROTO) - log_error_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); + log_debug_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); else if (r < 0) log_error_errno(r, "Remounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid); return r; } -static int umount_with_timeout(MountPoint *m, bool *changed) { +static int umount_with_timeout(MountPoint *m, int umount_log_level) { pid_t pid; int r; BLOCK_SIGNALS(SIGCHLD); + assert(m); + /* Due to the possiblity of a umount operation hanging, we * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular umount @@ -443,7 +479,7 @@ static int umount_with_timeout(MountPoint *m, bool *changed) { * then return EBUSY).*/ r = umount2(m->path, MNT_FORCE); if (r < 0) - log_error_errno(errno, "Failed to unmount %s: %m", m->path); + log_full_errno(umount_log_level, errno, "Failed to unmount %s: %m", m->path); _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } @@ -453,7 +489,7 @@ static int umount_with_timeout(MountPoint *m, bool *changed) { log_error_errno(r, "Unmounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid); (void) kill(pid, SIGKILL); } else if (r == -EPROTO) - log_error_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); + log_debug_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); else if (r < 0) log_error_errno(r, "Unmounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid); @@ -462,38 +498,15 @@ static int umount_with_timeout(MountPoint *m, bool *changed) { /* This includes remounting readonly, which changes the kernel mount options. * Therefore the list passed to this function is invalidated, and should not be reused. */ - -static int mount_points_list_umount(MountPoint **head, bool *changed) { +static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) { MountPoint *m; int n_failed = 0; assert(head); + assert(changed); LIST_FOREACH(mount_point, m, *head) { - bool mount_is_readonly; - - mount_is_readonly = fstab_test_yes_no_option(m->options, "ro\0rw\0"); - - /* If we are in a container, don't attempt to - read-only mount anything as that brings no real - benefits, but might confuse the host, as we remount - the superblock here, not the bind mount. - If the filesystem is a network fs, also skip the - remount. It brings no value (we cannot leave - a "dirty fs") and could hang if the network is down. - Note that umount2() is more careful and will not - hang because of the network being down. */ - if (detect_container() <= 0 && - !fstype_is_network(m->type) && - !mount_is_readonly) { - _cleanup_free_ char *options = NULL; - /* MS_REMOUNT requires that the data parameter - * should be the same from the original mount - * except for the desired changes. Since we want - * to remount read-only, we should filter out - * rw (and ro too, because it confuses the kernel) */ - (void) fstab_filter_options(m->options, "rw\0ro\0", NULL, NULL, &options); - + if (m->try_remount_ro) { /* We always try to remount directories * read-only first, before we go on and umount * them. @@ -508,16 +521,19 @@ static int mount_points_list_umount(MountPoint **head, bool *changed) { * somehwere else via a bind mount. If we * explicitly remount the super block of that * alias read-only we hence should be - * relatively safe regarding keeping dirty an fs + * relatively safe regarding keeping a dirty fs * we cannot otherwise see. * * Since the remount can hang in the instance of * remote filesystems, we remount asynchronously - * and skip the subsequent umount if it fails */ - if (remount_with_timeout(m, options, &n_failed) < 0) { - if (nonunmountable_path(m->path)) + * and skip the subsequent umount if it fails. */ + if (remount_with_timeout(m, umount_log_level) < 0) { + /* Remount failed, but try unmounting anyway, + * unless this is a mount point we want to skip. */ + if (nonunmountable_path(m->path)) { n_failed++; - continue; + continue; + } } } @@ -528,12 +544,10 @@ static int mount_points_list_umount(MountPoint **head, bool *changed) { continue; /* Trying to umount */ - if (umount_with_timeout(m, changed) < 0) + if (umount_with_timeout(m, umount_log_level) < 0) n_failed++; - else { - if (changed) - *changed = true; - } + else + *changed = true; } return n_failed; @@ -544,13 +558,12 @@ static int swap_points_list_off(MountPoint **head, bool *changed) { int n_failed = 0; assert(head); + assert(changed); LIST_FOREACH_SAFE(mount_point, m, n, *head) { log_info("Deactivating swap %s.", m->path); if (swapoff(m->path) == 0) { - if (changed) - *changed = true; - + *changed = true; mount_point_free(head, m); } else { log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); @@ -561,12 +574,13 @@ static int swap_points_list_off(MountPoint **head, bool *changed) { return n_failed; } -static int loopback_points_list_detach(MountPoint **head, bool *changed) { +static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) { MountPoint *m, *n; int n_failed = 0, k; struct stat root_st; assert(head); + assert(changed); k = lstat("/", &root_st); @@ -585,12 +599,12 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) { log_info("Detaching loopback %s.", m->path); r = delete_loopback(m->path); if (r >= 0) { - if (r > 0 && changed) + if (r > 0) *changed = true; mount_point_free(head, m); } else { - log_warning_errno(errno, "Could not detach loopback %s: %m", m->path); + log_full_errno(umount_log_level, errno, "Could not detach loopback %s: %m", m->path); n_failed++; } } @@ -598,12 +612,13 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) { return n_failed; } -static int dm_points_list_detach(MountPoint **head, bool *changed) { +static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) { MountPoint *m, *n; int n_failed = 0, r; dev_t rootdev; assert(head); + assert(changed); r = get_block_device("/", &rootdev); if (r <= 0) @@ -611,21 +626,18 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) { LIST_FOREACH_SAFE(mount_point, m, n, *head) { - if (major(rootdev) != 0) - if (rootdev == m->devnum) { - n_failed ++; - continue; - } + if (major(rootdev) != 0 && rootdev == m->devnum) { + n_failed ++; + continue; + } log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum)); r = delete_dm(m->devnum); if (r >= 0) { - if (changed) - *changed = true; - + *changed = true; mount_point_free(head, m); } else { - log_warning_errno(errno, "Could not detach DM %s: %m", m->path); + log_full_errno(umount_log_level, errno, "Could not detach DM %s: %m", m->path); n_failed++; } } @@ -633,16 +645,18 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) { return n_failed; } -static int umount_all_once(bool *changed) { +static int umount_all_once(bool *changed, int umount_log_level) { int r; LIST_HEAD(MountPoint, mp_list_head); + assert(changed); + LIST_HEAD_INIT(mp_list_head); r = mount_points_list_get(&mp_list_head); if (r < 0) goto end; - r = mount_points_list_umount(&mp_list_head, changed); + r = mount_points_list_umount(&mp_list_head, changed, umount_log_level); end: mount_points_list_free(&mp_list_head); @@ -650,17 +664,19 @@ static int umount_all_once(bool *changed) { return r; } -int umount_all(bool *changed) { +int umount_all(bool *changed, int umount_log_level) { bool umount_changed; int r; + assert(changed); + /* Retry umount, until nothing can be umounted anymore. Mounts are * processed in order, newest first. The retries are needed when * an old mount has been moved, to a path inside a newer mount. */ do { umount_changed = false; - r = umount_all_once(&umount_changed); + r = umount_all_once(&umount_changed, umount_log_level); if (umount_changed) *changed = true; } while (umount_changed); @@ -672,6 +688,8 @@ int swapoff_all(bool *changed) { int r; LIST_HEAD(MountPoint, swap_list_head); + assert(changed); + LIST_HEAD_INIT(swap_list_head); r = swap_list_get(&swap_list_head); @@ -686,17 +704,19 @@ int swapoff_all(bool *changed) { return r; } -int loopback_detach_all(bool *changed) { +int loopback_detach_all(bool *changed, int umount_log_level) { int r; LIST_HEAD(MountPoint, loopback_list_head); + assert(changed); + LIST_HEAD_INIT(loopback_list_head); r = loopback_list_get(&loopback_list_head); if (r < 0) goto end; - r = loopback_points_list_detach(&loopback_list_head, changed); + r = loopback_points_list_detach(&loopback_list_head, changed, umount_log_level); end: mount_points_list_free(&loopback_list_head); @@ -704,17 +724,19 @@ int loopback_detach_all(bool *changed) { return r; } -int dm_detach_all(bool *changed) { +int dm_detach_all(bool *changed, int umount_log_level) { int r; LIST_HEAD(MountPoint, dm_list_head); + assert(changed); + LIST_HEAD_INIT(dm_list_head); r = dm_list_get(&dm_list_head); if (r < 0) goto end; - r = dm_points_list_detach(&dm_list_head, changed); + r = dm_points_list_detach(&dm_list_head, changed, umount_log_level); end: mount_points_list_free(&dm_list_head); diff --git a/src/core/umount.h b/src/core/umount.h index 7c029c384c..a6613e6ad6 100644 --- a/src/core/umount.h +++ b/src/core/umount.h @@ -20,10 +20,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -int umount_all(bool *changed); +int umount_all(bool *changed, int umount_log_level); int swapoff_all(bool *changed); -int loopback_detach_all(bool *changed); +int loopback_detach_all(bool *changed, int umount_log_level); -int dm_detach_all(bool *changed); +int dm_detach_all(bool *changed, int umount_log_level); diff --git a/src/import/import.c b/src/import/import.c index 454e64e3cb..b94b196b87 100644 --- a/src/import/import.c +++ b/src/import/import.c @@ -208,7 +208,7 @@ static int import_raw(int argc, char *argv[], void *userdata) { fd = STDIN_FILENO; (void) readlink_malloc("/proc/self/fd/0", &pretty); - log_info("Importing '%s', saving as '%s'.", pretty, local); + log_info("Importing '%s', saving as '%s'.", strempty(pretty), local); } r = sd_event_default(&event); diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 07cb257151..4a85d751bc 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2197,7 +2197,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd cancelled = m->scheduled_shutdown_type != NULL; reset_scheduled_shutdown(m); - if (cancelled) { + if (cancelled && m->enable_wall_messages) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; const char *tty = NULL; uid_t uid = 0; diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 93648e1be0..fa054fcfc3 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -238,7 +238,7 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_h assert(link); assert(callback); - if (link->flags & IFF_UP) { + if (link->flags & IFF_UP && netdev->kind == NETDEV_KIND_BOND) { log_netdev_debug(netdev, "Link '%s' was up when attempting to enslave it. Bringing link down.", link->ifname); r = link_down(link); if (r < 0) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index ecb96cdb57..aaea2ae908 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -332,6 +332,11 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, link_set_dhcp_routes(link); + if (link->dhcp4_messages == 0) { + link->dhcp4_configured = true; + link_check_ready(link); + } + return 1; } diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 234e0a4602..f82e915399 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -202,11 +202,13 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, if (r < 0) return r; + route->family = AF_INET6; + while (n < n_prefixes) { route_update(route, &prefix, pd_prefix_len, NULL, NULL, 0, 0, RTN_UNREACHABLE); - r = route_configure(route, link, NULL); + r = route_configure(route, dhcp6_link, NULL); if (r < 0) { route_free(route); return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 64c45080df..31d3f7ade7 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2985,7 +2985,7 @@ network_file_fail: goto dhcp4_address_fail; } - r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize); + r = sd_dhcp_client_new(&link->dhcp_client, link->network ? link->network->dhcp_anonymize : 0); if (r < 0) return log_link_error_errno(link, r, "Failed to create DHCPv4 client: %m"); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 70dca5219b..8446feb316 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -379,14 +379,12 @@ void route_update(Route *route, unsigned char type) { assert(route); - assert(src); - assert(gw); - assert(prefsrc); + assert(src || src_prefixlen == 0); - route->src = *src; + route->src = src ? *src : (union in_addr_union) {}; route->src_prefixlen = src_prefixlen; - route->gw = *gw; - route->prefsrc = *prefsrc; + route->gw = gw ? *gw : (union in_addr_union) {}; + route->prefsrc = prefsrc ? *prefsrc : (union in_addr_union) {}; route->scope = scope; route->protocol = protocol; route->type = type; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 54b2137c9c..e58d318c76 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1989,6 +1989,8 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_ar if (q < 0 && r == 0) r = q; + errno = 0; /* Reset errno explicitly, so that log_debug_errno() will properly print 'Success' + * for q == 0, instead of whatever is set in errno */ log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); } diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 15cfe4e4f7..33340b1f20 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -409,6 +409,27 @@ int config_parse(const char *unit, continuation = mfree(continuation); } + if (continuation) { + r = parse_line(unit, + filename, + ++line, + sections, + lookup, + table, + flags, + §ion, + §ion_line, + §ion_ignored, + continuation, + userdata); + if (r < 0) { + if (flags & CONFIG_PARSE_WARN) + log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line); + return r; + + } + } + return 0; } diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 6ae97cf107..3666be61ec 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -4063,8 +4063,9 @@ static void print_status_info( char ** dropin; STRV_FOREACH(dropin, i->dropin_paths) { - if (! dir || last) { - printf(dir ? " " : " Drop-In: "); + if (!dir || last) { + printf(dir ? " " : + " Drop-In: "); dir = mfree(dir); @@ -4074,7 +4075,8 @@ static void print_status_info( return; } - printf("%s\n %s", dir, + printf("%s\n" + " %s", dir, special_glyph(TREE_RIGHT)); } @@ -8374,6 +8376,7 @@ static int talk_initctl(void) { _cleanup_close_ int fd = -1; char rl; int r; + const char *p; rl = action_to_runlevel(); if (!rl) @@ -8381,17 +8384,21 @@ static int talk_initctl(void) { request.runlevel = rl; - fd = open(INIT_FIFO, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + FOREACH_STRING(p, "/run/initctl", "/dev/initctl") { + fd = open(p, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + if (fd >= 0 || errno != ENOENT) + break; + } if (fd < 0) { if (errno == ENOENT) return 0; - return log_error_errno(errno, "Failed to open "INIT_FIFO": %m"); + return log_error_errno(errno, "Failed to open initctl fifo: %m"); } r = loop_write(fd, &request, sizeof(request), false); if (r < 0) - return log_error_errno(r, "Failed to write to "INIT_FIFO": %m"); + return log_error_errno(r, "Failed to write to %s: %m", p); return 1; #else diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h index f6fb40fbb5..425e38e2e2 100644 --- a/src/systemd/sd-bus-vtable.h +++ b/src/systemd/sd-bus-vtable.h @@ -157,8 +157,7 @@ struct sd_bus_vtable { { \ .type = _SD_BUS_VTABLE_END, \ .flags = 0, \ - .x = { \ - }, \ + .x = { { 0 } }, \ } _SD_END_DECLARATIONS; diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c index 72c53ab6a0..028bf5ef3b 100644 --- a/src/test/test-conf-parser.c +++ b/src/test/test-conf-parser.c @@ -291,6 +291,11 @@ static const char* const config_file[] = { "3\n", "[Section]\n" + "setting1=1\\\n" /* continuation with extra trailing backslash at the end */ + "2\\\n" + "3\\\n", + + "[Section]\n" "setting1=1\\\\\\\n" /* continuation with trailing escape symbols */ "\\\\2\n", /* note that C requires one level of escaping, so the * parser gets "…1 BS BS BS NL BS BS 2 NL", which @@ -307,6 +312,11 @@ static const char* const config_file[] = { "foobar", "[Section]\n" + "setting1=" /* a line above LINE_MAX length, with continuation */ + x1000("ABCD") "\\\n" /* and an extra trailing backslash */ + "foobar\\\n", + + "[Section]\n" "setting1=" /* a line above the allowed limit: 9 + 1050000 + 1 */ x1000(x1000("x") x10("abcde")) "\n", @@ -359,27 +369,27 @@ static void test_config_parse(unsigned i, const char *s) { assert_se(streq(setting1, "1")); break; - case 4: + case 4 ... 5: assert_se(r == 0); assert_se(streq(setting1, "1 2 3")); break; - case 5: + case 6: assert_se(r == 0); assert_se(streq(setting1, "1\\\\ \\\\2")); break; - case 6: + case 7: assert_se(r == 0); assert_se(streq(setting1, x1000("ABCD"))); break; - case 7: + case 8 ... 9: assert_se(r == 0); assert_se(streq(setting1, x1000("ABCD") " foobar")); break; - case 8 ... 9: + case 10 ... 11: assert_se(r == -ENOBUFS); assert_se(setting1 == NULL); break; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index cbacfae66b..608cfbb58f 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -879,6 +879,9 @@ static int path_set_perms(Item *i, const char *path) { if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to fstat() file %s: %m", path); + if (i->type == EMPTY_DIRECTORY && !S_ISDIR(st.st_mode)) + return log_error_errno(EEXIST, "'%s' already exists and is not a directory. ", path); + return fd_set_perms(i, fd, &st); } @@ -1591,7 +1594,7 @@ static int create_item(Item *i) { _fallthrough_; case EMPTY_DIRECTORY: - r = path_set_perms(i, i->path); + r = glob_item(i, path_set_perms); if (q < 0) return q; if (r < 0) diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index 9bdaef8d90..8179a5e2f8 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -418,7 +418,7 @@ static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_uset if (!u) return -ENOMEM; - ecmd.req = u->base; + u->base = ecmd.req; offset = 0; memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); diff --git a/units/systemd-binfmt.service.in b/units/systemd-binfmt.service.in index df9396d895..e940c7c9ad 100644 --- a/units/systemd-binfmt.service.in +++ b/units/systemd-binfmt.service.in @@ -10,7 +10,8 @@ [Unit] Description=Set Up Additional Binary Formats Documentation=man:systemd-binfmt.service(8) man:binfmt.d(5) -Documentation=https://www.kernel.org/doc/Documentation/binfmt_misc.txt +Documentation=https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html +Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems DefaultDependencies=no Conflicts=shutdown.target After=proc-sys-fs-binfmt_misc.automount diff --git a/units/systemd-initctl.service.in b/units/systemd-initctl.service.in index 6cfed3da11..2b4b957dce 100644 --- a/units/systemd-initctl.service.in +++ b/units/systemd-initctl.service.in @@ -8,7 +8,7 @@ # (at your option) any later version. [Unit] -Description=/dev/initctl Compatibility Daemon +Description=initctl Compatibility Daemon Documentation=man:systemd-initctl.service(8) DefaultDependencies=no diff --git a/units/systemd-initctl.socket b/units/systemd-initctl.socket index 61f877ba7d..9d97579908 100644 --- a/units/systemd-initctl.socket +++ b/units/systemd-initctl.socket @@ -8,12 +8,12 @@ # (at your option) any later version. [Unit] -Description=/dev/initctl Compatibility Named Pipe +Description=initctl Compatibility Named Pipe Documentation=man:systemd-initctl.service(8) DefaultDependencies=no Before=sockets.target [Socket] -ListenFIFO=/run/systemd/initctl/fifo +ListenFIFO=/run/initctl Symlinks=/dev/initctl SocketMode=0600 |