systemd / systemd

The systemd System and Service Manager
https://systemd.io
GNU General Public License v2.0
13.21k stars 3.78k forks source link

Run tmpfiles conditionally #21402

Open mbiebl opened 2 years ago

mbiebl commented 2 years ago

Many packages nowadays use a tmpfile to setup their runtime files. There are cases though, where a package is installed, but the corresponding service is not enabled, yet the tmpfile is processed during boot and where this can problems or is simply unnecessary. For a recent case, see #21317

I'd like the tmpfiles format to be extended. We add a new key there, like say Unit= (better names welcome)

Example: you have a /usr/lib/tmpfiles.d/foo.conf which should only be run if foo.service is enabled. You can add a Unit=foo.service key to /usr/lib/tmpfiles.d/foo.conf and the tmpfile would only be processed if foo.service is enabled.

This would have the benefit, that you can tie the processing of the tmpfile to the enablement state of the corresponding service/unit.

keszybz commented 2 years ago

Yeah, I had a similar thought. Or maybe we should allow tmpfiles snippets to be specified in unit files, and systemd would execute them (through some helper service probably) when starting the unit.

poettering commented 2 years ago

Let's not add complex stuff like this for such weak cases as #21317. If people don't want systemd-resolved to take over /etc/resolv.conf then great, but they should then declare what else is owning that file.

You don't resolve issues of ownership by not owning the file at all.

mbiebl commented 2 years ago

Let's not add complex stuff like this for such weak cases as #21317. If people don't want systemd-resolved to take over /etc/resolv.conf then great, but they should then declare what else is owning that file.

I think it's a valid use case.

You don't resolve issues of ownership by not owning the file at all.

systemd-resolved claiming the file when it is not even enabled is just plain and simple wrong.

mbiebl commented 2 years ago

That said, this is just a specific use case.

This disconnect between .service units and their corresponding tmpfiles has bugged me for a while.

DaanDeMeyer commented 2 years ago

It would be nice to have resolved be able to overwrite the file as well if enabled. On Arch resolv.conf is empty in the initial rootfs when installing and users always have to create the resolved symlink manually on top of enabling resolved. It'd be nice if enabling resolved implied installing the symlink as well.

poettering commented 2 years ago

I am pretty sure we should not put emphasis on associating system resources with services. Instead focus on making service resources truly service associated in the first place.

i.e. RuntimeDirectory=, StateDirectory=, … are a much better approach to most of what tmpfiles.d is used for. this is stuff with very clear semantics, and a much more focused usecase. It pushes people towards doing the right thing.

Hence, instead of adding glue between tmpfiles.d and services: don't. Just make people use RuntimeDirectory= and suchlike, it should cover 80% of what people misuse tmpfiles.d for.

The way I see it, tmpfiles.d should only be used for truly system-scoped files. If I however look into the files fedora packages actually install it's mostly packaged scoped files, and they really shouldn't use it for that. on my system here, it's colord, cups, gluster, hplip, httpd, iscsi, libgpod, podman, ppp, … that put daemon-specific stuff in tmpfiles snippets, that could as well just be RuntimeDirectory= + StateDirectory= stuff. (And i stopped looking, it's really the vast majority).

poettering commented 2 years ago

It would be nice to have resolved be able to overwrite the file as well if enabled.

Nah resolved is a consumer of the file happily, too. resolved is very defensively put together to be either provider or consumer, depending how you choose to create the symlink. Overriding that is breaking that ownership rule, and we shouldn't do that, given that we expressly support both scenarios: where we consume and where we provide that file.

mbiebl commented 2 years ago

Well, problem is, that RuntimeDirectory is not flexible enough (you can't even modify permissions). Sometimes services need more then just a runtime directory and its fixed semantics. That's the real world. RuntimeDirectory only works for the most trivial of cases.

mbiebl commented 2 years ago

Instead focus on making service resources truly service associated in the first place.

Right, that's exactly what is proposed here.

poettering commented 2 years ago

Well, problem is, that RuntimeDirectory is not flexible enough (you can't even modify permissions).

You can! RuntimeDirectoryMode= exists. What else do you need?

Sometimes services need more then just a runtime directory and its fixed semantics. That's the real world.

Yeah, for stuff that wants to manage stuff that belongs to the system special rules apply, but I am not sure we need to add additional infra for that: if your software can't accept ownership concepts, then that's OK but things won't be automatic and people shouldn't expect otherwise.

poettering commented 2 years ago

Instead focus on making service resources truly service associated in the first place.

Right, that's exactly what is proposed here.

No? You want to manage system resources in tmpfiles.d and bind that to service enablement/runtime, and I am not convinced that's desirable.

I am suggesting to move things that are currently managed as system resources into service scope, i.e. stop using tmpfiles.d at all, but only operate in directories specific to the services.

mbiebl commented 2 years ago

Well, problem is, that RuntimeDirectory is not flexible enough (you can't even modify permissions).

You can! RuntimeDirectoryMode= exists. What else do you need?

You can't A common use case is a shared runtime directory between different services which use a common group.

mbiebl commented 2 years ago

I am suggesting to move things that are currently managed as system resources into service scope, i.e. stop using tmpfiles.d at all, but only operate in directories specific to the services.

If tmpfiles snippets are supported inside a .service unit (as @keszybz suggested), that would work fine for me as well. It's just a different way of implementing it.

mbiebl commented 2 years ago

Well, problem is, that RuntimeDirectory is not flexible enough (you can't even modify permissions).

You can! RuntimeDirectoryMode= exists. What else do you need?

Multiple runtime directories is another example.

poettering commented 2 years ago

You can't A common use case is a shared runtime directory between different services which use a common group.

So you mean ownership, not permissions. Group ownership works fine though, too? Just use Group=? What am I missing?

poettering commented 2 years ago

Multiple runtime directories is another example.

Works fine, just specify as many as you want?

mbiebl commented 2 years ago

So you mean ownership, not permissions. Group ownership works fine though, too? Just use Group=? What am I missing?

You don't necessarily want the service executed as another User or Group, you just want to change the ownership of certain directories.

Example: you want a service which sets up the directories (not necessarily in /run or any of the hard-coded paths) with proper ownership and permissions for other services/programs being able to access them. The service itself could e.g. itself need root privs so.

As said RuntimeDirectory is often simply not flexible enough.

mbiebl commented 2 years ago

RuntimeDirectoryMode=

If I'm not mistaken, this applies to all directories specified via RuntimeDirectory=.

mbiebl commented 2 years ago

Last time I used RuntimeDirectory=, it also brutally removed the directory when the service was stopped. This doesn't work if you have multiple services/apps using the same directories

poettering commented 2 years ago

You don't necessarily want the service executed as another User or Group, you just want to change the ownership of certain directories.

Example: you want a service which sets up the directories (not necessarily in /run or any of the hard-coded paths) with proper ownership and permissions for other services/programs being able to access them. The service itself could e.g. itself need root privs so

The way this is covered is through ExecStart=!. i.e. if the service shall be invoked as root even though there's a user associated with it, then declare this via the !. This is for example the case if services intend to drop privs on their own.

As said RuntimeDirectory is often simply not flexible enough.

So far the cases you listed are all covered afaics.

But yes, there are certainly cases where RuntimeDirectory= cannot express what people want. But I'd say it's OK if those cases are not as pretty, because what you are doing then is simply not pretty, we shouldn't hide that. We should push people towards not doing things like that, and having clear concepts of services, users and directories and how they belong together.

As I mentioned I am pretty sure RuntimeDirectory= (and related settings) if properly used can cover 80% of the cases tmpfiles.d is currently used for. The 20% exist, but I am not convinced it's worth adding "pretty glue" for them. They should be a bit ugly and less smooth, so that people stop doing things that way.

poettering commented 2 years ago

Last time I used RuntimeDirectory=, it also brutally removed the directory when the service was stopped. This doesn't work if you have multiple services/apps using the same directories

RuntimeDirectoryPreserve=

poettering commented 2 years ago

btw, if you really want you can already embed tmpfiles.d snippets in your own unit. Add a helper unit like this:

[Unit]
Description=Preparation for My Service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/systemd-tmpfiles --create --remove -
StandardInputText= \
        w /run/foo 0755 root root - "bar" \
        …

And then pull that in from your service

mbiebl commented 2 years ago

So far the cases you listed are all covered afaics.

Nope, not really. But I guess you've already made up your mind, so its pointless to discuss this. Of course we could wish the world was prettier and ignore existing software and hope they would all behave the same, but they often don't.

mbiebl commented 2 years ago

As I mentioned I am pretty sure RuntimeDirectory= (and related settings) if properly used can cover 80% of the cases tmpfiles.d is currently used for. The 20% exist, but I am not convinced it's worth adding "pretty glue" for them. They should be a bit ugly and less smooth, so that people stop doing things that way.

I quickly checked the Debian archive. According to codesearch.debian.net we have a 100 packages using RuntimeDirectory. On the other hand we have 173 packages installing 185 tmpfiles. And there is a good bunch of services that run ExecStartPre=install -d (or a variation thereof).

So either all of those software maintainers are too stupid to understand the documentation, our documentation sucks or simply RuntimeDirectory= doesn't meet their needs.

poettering commented 2 years ago

I will agree with you that the RuntimeDirectory= feature set is not well advertised. I should probably do a blog story about it, i.e. pointing people to the fact that RuntimeDirectoryMode= RuntimeDirectoryPreserve= exists, and that ExecStart=! exists and is useful for this.

mbiebl commented 2 years ago

btw, if you really want you can already embed tmpfiles.d snippets in your own unit. Add a helper unit like this:

[Unit]
Description=Preparation for My Service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/systemd-tmpfiles --create --remove -
StandardInputText= \
        w /run/foo 0755 root root - "bar" \
        …

And then pull that in from your service

Well, aside from requiring a lot of boilerplate this is also super ugly. I don't consider this a "solution".

pemensik commented 2 years ago

It would be nice to have resolved be able to overwrite the file as well if enabled. On Arch resolv.conf is empty in the initial rootfs when installing and users always have to create the resolved symlink manually on top of enabling resolved. It'd be nice if enabling resolved implied installing the symlink as well.

Empty file is not really useful. I think it might be workaround for what I am trying to fix in MR #21317. If it were missing, which is functionally equivalent, my proposal would solve that. Can you find a reason for empty file in its package changelog?

bluca commented 2 years ago

So you mean ownership, not permissions. Group ownership works fine though, too? Just use Group=? What am I missing?

You don't necessarily want the service executed as another User or Group, you just want to change the ownership of certain directories.

Example: you want a service which sets up the directories (not necessarily in /run or any of the hard-coded paths) with proper ownership and permissions for other services/programs being able to access them. The service itself could e.g. itself need root privs so.

As said RuntimeDirectory is often simply not flexible enough.

Adding idmapping support for runtime and state directory is on my to-do list and I should get to it next month. That will make ownership issues moot.

In 250 i also added support for src:dst via symlinks, so that different services can use the same resource even using different names.