-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Applying default/distros presets override sysadmin previous configuration #4830
Comments
Can you restate the problem that you're trying to solve?
I don't want to go over old mailing list threads, and your mail does
not make it clear.
Zbyszek
|
Sure: It might be desired to re-apply a preset during a package upgrade because the default shipped during the installation was considered as boggus. But it would be nice to apply the new preset config without destroying the sysadmin's configuration (if any). For example if the user decided to disable the service "foo" after giving it a try then even if the distro default is changed from "disabled" to "enabled" for that service later shouldn't have any effect since the user explicitly said "I want foo to be disabled". You could also find a relevant discussion about this here: https://lists.freedesktop.org/archives/systemd-devel/2014-November/025288.html |
On Tue, Dec 06, 2016 at 08:01:03AM -0800, Franck Bui wrote:
Sure:
For example if the user decided to disable the service "foo" after giving it a try then even if the distro default is changed from "disabled" to "enabled" for that service later shouldn't have any effect since the user explicitly said "I want foo to be disabled".
You could also find a relevant discussion about this here: https://lists.freedesktop.org/archives/systemd-devel/2014-November/025288.html
I (re-)read that discussion. Various good points are raised, and
certainly are cases where something more nuanced then the current
preset-based approach would be useful, but I think that we're pretty
far from having a convincing argument for adding new functionality.
There's a cognitive cost, and also potential for bugs, even if we
ignore the implementation effort, to any new scheme. I'll like to remind
everyone that current scheme with symlinks has seen a metric ton of bugs,
implementation shortcomings, etc. Adding another layer of configurability
should imho require a good justification.
I'm also not convinced about the central premise:
It might be desired to re-apply a preset during a package upgrade
because the default shipped during the installation was considered
as boggus. But it would be nice to apply the new preset config
*without* destroying the sysadmin's configuration (if any).
We cannot distinguish the case when the administrator didn't care if
some service was enabled (e.g. because the functionality wasn't needed
for anything), from the case when the administrator noticed that the
service is in the right state, either enabled and disabled, and thus
didn't take any action.
In the current scheme in which presets which get applied only during
initial package installation, we implicitly assume the second of those
two cases. The rule is simple — initial service enablement state is
preserved over upgrades. IIUC, the proposed change is to assume that
the first is true and (sometimes) change enablement state on upgrades.
I don't think that's better — that scheme is more tricky, and harder
to explain and understand.
Apart from that central scheme, there were some concrete proposals:
- allow masking of .wants symlinks in /usr/lib by /dev/null links in
/etc.
This sounds potentially useful, even for other uses. It'd change
current behaviour, but current behaviour is just an undocumented
accident of implementation, which nobody sane should exploiting.
- when a unit has an [Install] section, but also carries it own symlinks
in /usr/lib/systemd/*/*.wants or /usr/lib/systemd/*/*.requires,
systemctl list-unit-files displays it as "disabled", and "static"
was suggested instead.
Colin was against changing current behaviour, and I (weakly) agree.
There are many ways in which units can be pulled in ([Install]
section, static symlinks in /usr/lib/systemd/*/*.wants,
Wants=/Requires= in other units, udev rules, kernel commandline,
generators, explicit systemctl start from scripts, etc), so it's
always possible for a unit to be active without having the symlinks
from [Install] present. We cannot even detect all of those ways, so
I think it's fine to only look at [Install] sections.
A sub-issue is whether systemctl enable/preset should skip creating a
duplicate symlink in /etc if an equivalent symlink exists in /usr/lib.
And also whether is-enabled should consider the symlink in /usr/lib
as satisfying what is specified in [Install]. To both of those, I'd
say yes.
To sum it up, I'd like to see some incremental improvements, but I
feel the big picture is correct.
Zbyszek
PS. I don't think the Debian preset of 'enable *' is useful. I find
the Fedora approach in which only services which are by default enabled
are those which are "safe" and don't require configuration and provide
some general functionality which is expected to be present if the
package is installed. I have a bunch of symlinks in /etc, and they
genuinely reflect local configuration of my machine and they don't
bother me at all.
|
On 12/07/2016 02:02 AM, Zbigniew Jędrzejewski-Szmek wrote:
I (re-)read that discussion. Various good points are raised, and
[...]
implementation shortcomings, etc. Adding another layer of configurability
should imho require a good justification.
Agreed.
But that's the reason why I proposed a different approach which looks
much simpler ( maybe too simpler ;) ). But unfortunately you haven't
commented on that...
We cannot distinguish the case when the administrator didn't care if
some service was enabled (e.g. because the functionality wasn't needed
for anything), from the case when the administrator noticed that the
service is in the right state, either enabled and disabled, and thus
didn't take any action.
In the current scheme in which presets which get applied only during
initial package installation, we implicitly assume the second of those
two cases. The rule is simple — initial service enablement state is
preserved over upgrades. IIUC, the proposed change is to assume that
the first is true and (sometimes) change enablement state on upgrades.
I don't think that's better — that scheme is more tricky, and harder
to explain and understand.
I agree.
But another point of view would be that if the sysadmin hasn't
configured anything then it means that he accepted to follow the distro
defaults which might change in the future.
We could also teach them that in order to keep one current default
across package upgrades, he could define his own presets (in /etc/).
That would be equivalent to the alternative I proposed basically but
without doing it automatically if the user explicitly enable a service.
|
Yeah, I pretty much agree with @keszybz. Please file PRs for the /dev/null symlinks for deps, and stuff like that. But I must say making presets "dynamic" sounds wrong to me. Presets are supposed to be static really, and not record user configuration. |
But the same reasoning about not being able to distinguish the case of implicit approval with just not caring applies here. I know Debian likes to record origin of various decisions and base subsequent changes based on that, but I don't think we want this complexity.
I still think it'd change current preset system to something preset-like, with an additional level of enablement: explicitly enabled by the user. I see the appeal, but I think it's just too complex and thus not worth it. |
Indeed this was discussed several times already, last time IIRC on the second-to-last hackfest in Brussels. The One idea back then was to ship enablement symlinks in packages in The other aproach was @xnox' idea of "runtime presets" as @fbuihuu already mentioned. While these are more flexible, this means more boot-time overhead (you need to process more service files and create the links). I can't remember any more whether there was something conceptually wrong with this or @xnox just dropped this because of changing company. |
@martinpitt there is code out there and I have used runtime presets for a while, however I ended up optimizing that distro to the point that we simply shipped pre-enabled services as symlinks in |
I think what @martinpitt describes is an "incremental enhancement" to current code. It requires:
Later on, we could extend systemctl to allow creating symlinks in /usr/lib instead of /etc to make creation of packages easier. This would be simple change that would make the whole process easier, but isn't strictly necessary. I like this approach much more. It requires only small changes, and is backwards compatible (in spirit and implementation) with current scheme. |
@poettering then I'm not sure to understand the point of allowing presets to be located in /etc/systemd/system-preset/ |
On 12/09/2016 03:44 PM, Zbigniew Jędrzejewski-Szmek wrote:
I think what @martinpitt <https://github.com/martinpitt> describes is an
"incremental enhancement" to current code. It requires:
1. systemd support for masking .wants symlinks, which everybody seems
to agree would be desirable
ok.
Just to make sure: to create the /dev/null symlinks in /etc, one would
use "systemctl disable", right ?
2. support in the package system to create symlinks in /usr/lib
So there's no block to implementing this currently.
Do you mean modify the rpm macros so they install symlinks in /usr/lib ?
Later on, we could extend systemctl to allow creating symlinks in
/usr/lib instead of /etc to make creation of packages easier. This would
be simple change that would make the whole process easier, but isn't
strictly necessary.
Why not making "systemctl preset" create symlinks in /usr/lib and
"systemctl enable" in /etc ?
After all "preset" is supposed to used by packages, isn't it ?
|
That's two separate steps really:
Definitely we should do both, but step two doesn't have to be done immediately.
Package %post scripts should not modify in anything in /usr/lib. If there are any symlinks in /usr/lib, they should be part of the contents of the package. Again, two steps:
Because both |
Then how exactly you suggest to distinguish between unit, enabled by preset, and unit, enabled by admin? Making preset operate on /usr/lib and explicit enable/disable on /etc provides clean separation. It also goes in line with system hardening used by many distributions which modifies ownership/permissions of files under packaging control after installation. So you could switch between "permissive" and "locked down" presets without requiring package changes and still respecting current admin choice. |
Moreover that sounds to defeat the whole purpose of the preset mechanism: keep the default enablement policies in one place that are under control of one single package which may be different according the distro flavor. In the new scheme, "systemctl preset" really looks to be packaging material stuff only. |
My proposal does not in any way limit what is currently possible and used, so it cannot "defeat the whole purpose of the preset mechanism". One can still use it exactly as before, if wanted. The only difference is that it adds a mechanism for the distribution to package its symlinks in /usr/lib in a clean way, so that if the local configuration exactly matches the distribution defaults, no symlinks are necessary in /etc. The symlinks created in the new scheme have exactly the same effect as what now would be created after installation in /etc, except that they are packaged statically and land in /usr/lib.
By the location of the enablement symlinks, see above.
No. Presets are mostly for packaging stuff, when doing local changes normally it's easier to just do enable/disable instead of providing local presets. Nevertheless, the admin can always do so. Those presets would still be honoured, exactly the same as before.
It doesn't, unless you disallow use of presets by the admin. If you can have local presets in /etc, it would totally mess up the separation if they influenced what is present in /usr/lib. I think it's crucial that any symlinks created after installation based on local configuration are in /etc.
"Locked down"? I think that's a completely different story or just a wrong term. I feel I'm being misunderstood. Maybe an example would help:
As you can see, the postinstallatin/pre-uninstallation scriptlets are called as before, except that now, if the admin didn't override the distro config as supplied by When writing this, I realized that to nicely implement
This assumes that |
@keszybz: Right, that's pretty much what I had in mind. Changing a package to ship an enablement symlink in So if @fbuihuu does not insist to use presets for this, but is only interested in the actual effect we can retitle this accordingly, like "enabling a unit by default should not require link in /etc"? |
On 12/11/2016 10:26 PM, Zbigniew Jędrzejewski-Szmek wrote:
In the new scheme, "systemctl preset" really looks to be packaging
material stuff only.
No. Presets /are/ mostly for packaging stuff, when doing local changes
normally it's easier to just do enable/disable instead of providing
local presets. Nevertheless, the admin can always do so. Those presets
would still be honoured, exactly the same as before.
Ok so we mostly agree that presets should be used on package
installation/upgrade only.
For the admin, there's actually no gain to use that since enable/disable
is a more natural way to override defaults specially if we can get rid
of its current limitation.
That's true that the preset command has been there since a long time and
I'm not asking for hiding it from the administrator completely because
of the backward compatibility reason (TM).
But at least we should make it usable by the packaging stuff:
systemctl preset --vendor foo.service
would create the symlink in /usr/lib/. OTOH removing "--vendor" will
create the symlinks in /etc.
As it turns out currently, it seems that you really want to stop using
"systemct preset" from the packaging stuff whereas it was designed for
this primarly usage as I understood it.
I feel I'm being misunderstood. Maybe an example would help:
Let's say I'm packaging foobar.service. In the package creation script I
do (using rpm as an example, I'm most familiar with that):
%install
# install the service file as before, no change
install foobar.service -Dpm0644 -t %buildroot%_unitdir/
# precreate the enablement symlinks, I'm ignoring the implementation of service_should_be_enabled for now
%if service_should_be_enabled
systemctl enable foobar.service --root=%{buildroot} --vendor
%endif
Well, it seems that
systemctl preset --root=xxx --vendor foobar.service
is exactly what you want here.
When writing this, I realized that to nicely implement
|service_should_be_enabled|, we should query system presets. So
|service_should_be_enabled| could be imlemented as:
What exactly will it bring us to make the symlinks part of the package
except that we will have to put additional statements in *all* packages
such as:
%files
%_unitdir/foobar.service
%if service_should_be_enabled
%_unitdir/*.wants.d/foobar.service
%endif
OTOH simply changing %systemd_post macro to include the "--vendor"
option when calling "systemctl preset" and do that during package
installation *and* upgrade, will be the *only* needed change.
|
@fbuihuu is right in that regard, that's in fact what automatically generated maintainer scripts call, not |
No, I never said that.
The issue that I was trying to solve here was how to initialize the preset policy in %{buildroot}. With my proposed version, the preset state would be retrieved from the "host" system. %{buildroot} is empty, it contains only files installed by the package being built, so it doesn't have any preset policy files. For We need to query the distro preset policy for a unit, and then apply that preset policy in %{buildroot}. |
On 12/12/2016 03:46 PM, Zbigniew Jędrzejewski-Szmek wrote:
The issue that I was trying to solve here was how to initialize the
preset policy in %{buildroot}.
I think you still haven't explained why... and that's probably source of
my confusion.
With my proposed version, the preset
state would be retrieved from the "host" system. %{buildroot} is empty,
it contains only files installed by the package being built, so it
doesn't have any preset policy files.
So you basically embed the policies (symlinks) in the package whereas
the current way is to apply them during the package installation
according to the distro presets.
Again I'm not sure to understand why you want to change that as it will
require to rebuild each package for which the default policies are
changed later and add some complexities in each packages.
For |preset --vendor| to do
something useful, those policy files would have to be installed into
%{buildroot} prior to that command. But that's messy, since we don't
want them to end up part of the package.
Well simply replace "--root=%buildroot" with "--prefix=%buildroot" and
the policy files used will be located in the chroot used to build the
package (instead of being located in %{buildroot})
|
What @martinpitt said: "enabling a unit by default should not require link in /etc", while keeping to the other constraints: backwards compatibility, ability to perform local configuration both through enable/disable and presets, and that local configuration does not change /usr/lib.
Well, I don't think you can have both: if the default configuration is embedded in the package, changing it requires the package to be changing.
I don't think this is enough, because we need to operate on two locations (if we don't copy the files into the installation root). I think it might be cleaner to simply allow the list of preset files to be explicitly specified (Something like |
On 12/12/2016 10:27 PM, Zbigniew Jędrzejewski-Szmek wrote:
What @martinpitt <https://github.com/martinpitt> said: "enabling a unit
by default should not require link in /etc", while keeping to the other
constraints: backwards compatibility, ability to perform local
configuration both through enable/disable and presets, and that local
configuration does not change /usr/lib.
That's also possible without cluttering all the packages (to embed the
symlinks) and doing the generation of the symlinks (in /usr/lib) during
package installation, I think.
|
I am not sure I follow what this is about. But I am very much against making systemctl write to /usr, ever. That's just wrong, /usr is supposed to be a read-only thing where packages or "make install" or something like that drop files, but not something we make changes to with "systemctl". presets are a concept that exists only so that a vendor-recommended enable/disable state can be queried at any time, and be applied automatically at package installation time. Yes, admins can also install their preset files, but that only makes sense if they build their own images, i.e. in a relatively advanced workflow, where the admins kinda take the position of the image vendor, and are not just administrators of it anymore. If they are simply administrators of it, they should just use "systemctl enable" and "systemctl disable" to deviate from the vendor defaults. And they can use "systemctl preset" to return to the vendor defaults. However, any scheme that dynamically makes changes to the presets is just wrong in my opinion. If you want to change something dynamically, don't bother with the presets, just enable/disable your service you service directly. if a unit file in package version 1 had a different [Install] section than in version 2, and at upgrade the new settings shall be applied, then the postinst scripts should execute something like: In order to support stateless systems that want to carry nothing in /etc, I think it would make sense to add support for masking dependencies in /etc via /dev/null symlinks, as has been proposed. But that is mostly something for systems where presets are an undesirable concept, i.e. where the vendor default comes anyway already pre-applied in /usr, and hence the second level of vendor suggestions that presets encode are unnecessary. Anyway, I am not following the discussion here I must say. systemctl should not be able to modify /usr, and it should not ever spit out preset files. Form the three levels of enablement:
it should only change the third level possibly taking the second level into consideration and definitely the first level. Making systemctl modify the second level is wrong, and so is modifying the first level. if you want to modify the second level use a text editor when building your image. and when modifying the first level, then use a build script and "ln -s". systemctl should only be in the business of modifying the third level. Or to say this differently: I think systemctl should be an end-user/admin tool, not a build tool. |
@poettering: Indeed, I think we violently agree here :-) |
I. e. the idea was that a downstream packaging tool (such as Debian's |
The idea is that if you are creating symlinks in /usr/lib at packaging time, like we do during systemd installation btw, instead of doing it "manually" with I think when you say that
I never said anything about spitting out preset files. I have no idea where this came from. Anyway, I think that we agree on some basic implementation bits, so we should start with those, and then hopefully the big picture will be clearer. |
@keszybz hmm, so if I get this right, then you want to use systemctl in a special "build-time mode", in which case it does what we in systemd currently do with "ln -s" in our Makefile? I am not totally opposed to that, but also I think "ln -s" is probably mostly fine for this purpose, no? i mean, building requires an additional level of expertise, and people do it with build scripts anyway, hence doing "ln -s" sounds OK, no? I am mostly afraid that people would be more confused if we'd start exposing "systemctl --build" or so, and would begin misusing that for horrible things, and run it on an end-user systems or so, and they really shouldn't... if this option is really desirable, then maybe as a hidden env var or so, and with some extra checks (for example, require that --root= is also used...). Or maybe in a different tool "systemd-build" or so.. Anyway, i'd be very much onboard with:
(I am not sure anyone actually asked for item 2 or 3 in this list, but I got the impression) |
I'm not a big fan of modifying behaviour through environment variables. That's a lousy user interface.
I did, above:
|
That wouldn't work. "systemctl is-enabled foo" cannot differentiate between is-enabled-by-admin and is-enabled-by-preset. Imagine that preset author decides to disable foo. It was enabled in past => this will re-enable it, exactly in opposite you need. Me, as a package maintainer, do not care about exact implementation. I just need to implement: IF the service was explicitly enabled/disabled by sysadmin THEN keep the state on upgrade, independently on preset change. IF the service was enabled/disabled by preset THEN follow the preset To implement that, we need to save sysadmin actions in a different way/place than enabling/disabling by preset. If you will implement something like "systemctl is-enabled-by-admin foo" returning yes/no/default, then it would work. If you will implement "systemctl vendor-preset" that will keep sysadmin's decision, it would work as well. If you will implement dynamic preset file tracking sysadmin's "systemctl enable", it would work as well. There are many ways to do it. |
@stanislav-brabec well, we don't really record who did what, and I am not convinced we should, because if the admin runs a script that includes "systemctl" invocations, I figure you want this to be recorded as "admin" actions. But if the same script is called from some postinst packaging scripts, i figure it should be recorded as "packaging" actions. So, distuingishing these two cases is hard, as it would require inheriting context, and I am not sure we should really try to cover that. Moreover, the distinction "admin ran systemctl" vs "packaging scripts ran systemctl" sounds like something where the next guy would show up asking for even further distinction, maybe "user script ran systemctl" or "app1 ran systemctl vs. app2 ran systemctl"... Also, if a preset in version x of a distro said "enable service foo by default", and then the user upgrades to version y of the distro, which changed the preset to "disable service foo by default", then I doubt we should turn off foo. "presets" are a tool to pick user defaults if none exist yet. But from the moment on the preset is applied it's really a user setting I think, and the distinction whether the user or the packaging script ultimately enabled it shouldn't matter. Or to say this differently: I really not convinced that there should be any concept of "ownership of enablement" really in systemd. |
@poettering: I agree, the only really needed information is: The default defined by the package was left, package upgrade script should keep the state despite the new default.
I think that we should. I can imagine that a service can be deprecated in the new version, and we want to turn it off for everybody who don't care. (But such change did not appear yet in SUSE. Only reverse requests: change off to on.)
I disagree. Such approach makes impossible to fix bugs saying: You did not enable service foo by default. You can easily fix it for new install (add a line for preset), but there is no way to fix it for upgrades:
Me too. Package maintainer does not need "ownership". Package maintainer needs to be able to allow two states:
Any explicit "systemctl enable/disable" (done by syadmin, by script, but not by "systemctl preset") should change state from the first to the second one. |
On 12/13/2016 06:11 PM, Stanislav Brabec wrote:
Me, as a package maintainer, do not care about exact implementation. I
just need to implement:
[...]
To implement that, we need to save sysadmin actions in a different
way/place than enabling/disabling by preset.
Well I think we already agreed on this: /usr is for packagers and /etc
is sysadmin lands.
The missing pieces here are:
- masking /usr dependencies with /dev/null symlinks in /etc so
sysadmin can disable a unit (enabled by default by the presets in
/usr). Since it's done in /etc, it will be recorded as "sysadmin
customisation" and won't be later overriden by package updates
(since those will happen in /usr).
- package installations/updates only install symlinks in
/usr. Currently this is done by using "systemctl preset" which
operates on vendor presets (mostly) but enable units in /etc.
- unconditionally apply the preset default during package updates so
any changes in either their preset or in their [Install] section is
propagated to /usr and is actual only if not overidden already by
/etc.
I think we were discussing with @keszybz on the second item as he
wanted to implement the creation of the symlinks during package
*build* time. The given example however still relies on systemctl
creating symlinks in /usr.
I haven't really understood the advantages to do that at build time as
it will put some burden on package maintainers as they need to parse
the unit file, and explicitly track the symlinks only if the preset
for their package enables them. But the preset default will be kept
in a (separate) "branding" preset package (at least this is how it's
done for Suse distros currrently).
IMHO, having the /usr symlinks created during package installations or
updates and having a (new ?) command for packagers to create them is
simpler (at least for starting) because it will require no changes in
packages.
An idea was to use "systemctl preset" to create the symlinks only in
/usr by introducing a special mode/option (--vendor in the previous
examples) which should be used by packagers *only*. But @poettering
seems to strongly disagree with this.
|
Hi all, I took a look at this during this week to see how this could be done and it seems that most of the troubles are coming from aliases. Let's consider this corner case which illustrates the problem. There are: unit 'B', unit 'A' and its 2 aliases 'a1' and 'a2'. 'B' pulls in 'A' through different dropins as shown below:
Dropin 'a1' has been masked. Should 'A' still be pulled in through the other dropins or should the dropin mask also disable all other dropins pulling 'A' ? Before going into this I'd like to be sure that this use case makes sense. Thanks for the feedback. |
@fsateler do you mean:
should mask all And:
should mask all @keszybz , WDYT ? |
If the dropins are in
Yes, as long as the dropins are from |
This patch allows masking of .wants symlinks in /usr/lib by /dev/null symlinks in /etc. IOW it gives the possibility to mask a dependency symlink (in .wants or .requires), assuming that the later being located in a directory having a 'lower' precedence. As a consequence a dependency symlink mask in /etc/ will mask all equivalent dependency symlinks located in directories such as /run, /usr/lib. And a dependency symlink mask in /usr/lib will has no effect on a dropin symlink located in /run or /etc. Consider the following example: /etc/systemd/system/foo.service.wants/bar.service -> /dev/null /usr/lib/systemd/system/foo.service.wants/bar.service -> ../bar.service service 'foo' was installed during a package installation with a dependency on 'bar' and later sysadmin decided to disable this dependency by adding a mask in /etc/. This results in service 'bar' not being pulled in by 'bar' anymore. The primary use case is to allow a clean separation between service activation/deactivation done during package installations/updates and those done by sysadmin. Assuming that distro dependency symlinks happens in /usr/lib only, the sysadmin is now able to disable persistently a service by creating a mask in /etc/. This mask will take precedence over any distro policies (usually defined by presets) defined in /usr/lib. The distro policies (presets) are now free to be changed without interfering with the configuration done by the sysadmin (if any). They will still be preserved and will still be taken into account. The assumption on distros creating symlinks in /usr/lib only is currently not met since this happens in /etc/. However the dropin symlink mask is the first step to achieve this clean separation. Later changes will ensure that dropin symlinks created during package installations/updates will happen in /usr/lib only. See issue systemd#4830. In order to implement the masking of dependency symlinks properly, this patch defers the handling of those dependencies until all involved units are fully loaded. This way all units and especially their aliases are known and a mask can be effective on a specific unit including all of its aliases. This is due to the way we currently handle the unit aliases (how a unit and its aliases are "lazily" merged) and also due to the fact that we can't remove dependencies added to a unit (until a full daemon-reload happens). As an example: /etc/.../a.service.wants/b1.service -> /dev/null /usr/.../a.service.wants/b.service -> ../b.service and 'b1' is an alias of 'b'. In this example, dropin symlink dependencies pulling 'b' or any of its aliases (including 'b1') will be masked. This patch also changes a rather odd behavior that nobody sane should rely on: a .wants symlink to /dev/null was masking the unit the symlink was refering to. Therefore: /etc/.../a.wants/b.service -> /dev/null would have masked 'b' service. Whereas now, it only masks the corresponding dependency symlinks defined in a.wants/ directories. It also changes the following weird case: /etc/.../a.service.wants/b.service -> ../c.service where 'b' defines an alias for 'c'. I'm not sure why symlinks were used to define dependencies in .wants/.requires in the first place whereas a simple empty file would have been sufficient and unambiguous. With this patch applied, no more aliases are defined this way.
Hi, I tried to implement the mask thing in #5195. it would be nice if you could have a look. Thanks. |
(Sorry for the delay.)
No. I think it should only add the dependency added by that specific file, i.e. that null link nullifies the effect of any B.service.wants/a1.service symlink, and nothing else.
No, for the same same reason as above. In general, configuration in systemd is only additive. Allowing a mask to subtract configuration that is not defined in a masked file would go against that. |
@keszybz I think there's a confusion here due to my poor wordings: I used the term "dropin dependency" or even "dropin" assuming that in this context they were referring to dependency symlinks put in .wants or .requires directories. However "dropin" is the term used for all additional .conf files located in .service.d/, at least that is what you seem to assume. So I'll change that in the [RFC] I posted, and will use "dependency symlinks". Actually I'm even not sure this term is appropriate because we could only use an empty file instead of a symlink (except for masks) which would remove any ambiguities. Actually doing |
No, I don't think there was confusion like that. IIUC, we had a different interpretation of what exactly a mask file (e.g. symlink /etc/systemd/system/a.service.wants/b.service → /dev/null) should mask. In your interpretation, that'd be all Wants=b.service dependencies that a.service may have. In my interpretation that'd be just any /**/systemd/system/a.service.wants/b.service symlinks with lower priority). |
On 02/01/2017 02:23 PM, Zbigniew Jędrzejewski-Szmek wrote:
No, I don't think there was confusion like that. IIUC, we had a
different interpretation of what exactly a mask file (e.g. symlink
/etc/systemd/system/a.service.wants/b.service → /dev/null) should mask.
Hm, I don't remember we did.
In your interpretation, that'd be all Wants=b.service dependencies that
a.service may have. In my interpretation that'd be just any
/**/systemd/system/a.service.wants/b.service symlinks with lower priority).
But in any cases your interpretation is the correct one and it's the one
which is implemented.
|
This patch allows masking of .wants symlinks in /usr/lib by /dev/null symlinks in /etc. IOW it gives the possibility to mask a dependency symlink (in .wants or .requires), assuming that the later being located in a directory having a 'lower' precedence. As a consequence a dependency symlink mask in /etc/ will mask all equivalent dependency symlinks located in directories such as /run, /usr/lib. And a dependency symlink mask in /usr/lib will has no effect on a dropin symlink located in /run or /etc. Consider the following example: /etc/systemd/system/foo.service.wants/bar.service -> /dev/null /usr/lib/systemd/system/foo.service.wants/bar.service -> ../bar.service service 'foo' was installed during a package installation with a dependency on 'bar' and later sysadmin decided to disable this dependency by adding a mask in /etc/. This results in service 'bar' not being pulled in by 'bar' anymore. The primary use case is to allow a clean separation between service activation/deactivation done during package installations/updates and those done by sysadmin. Assuming that distro dependency symlinks happens in /usr/lib only, the sysadmin is now able to disable persistently a service by creating a mask in /etc/. This mask will take precedence over any distro policies (usually defined by presets) defined in /usr/lib. The distro policies (presets) are now free to be changed without interfering with the configuration done by the sysadmin (if any). They will still be preserved and will still be taken into account. The assumption on distros creating symlinks in /usr/lib only is currently not met since this happens in /etc/. However the dropin symlink mask is the first step to achieve this clean separation. Later changes will ensure that dropin symlinks created during package installations/updates will happen in /usr/lib only. See issue systemd#4830. In order to implement the masking of dependency symlinks properly, this patch defers the handling of those dependencies until all involved units are fully loaded. This way all units and especially their aliases are known and a mask can be effective on a specific unit including all of its aliases. This is due to the way we currently handle the unit aliases (how a unit and its aliases are "lazily" merged) and also due to the fact that we can't remove dependencies added to a unit (until a full daemon-reload happens). As an example: /etc/.../a.service.wants/b1.service -> /dev/null /usr/.../a.service.wants/b.service -> ../b.service and 'b1' is an alias of 'b'. In this example, dropin symlink dependencies pulling 'b' or any of its aliases (including 'b1') will be masked. This patch also changes a rather odd behavior that nobody sane should rely on: it's possible to define an alias for unit 'c' via a dependency symlink: /etc/.../a.service.wants/alias-for-c.service -> ../c.service In this case 'alias-for-c' is an alias of 'c' assuming that 'alias-for-c' unit doesn't exist. I'm not sure why symlinks were used to define dependencies in .wants/.requires in the first place whereas a simple empty file would have been sufficient and unambiguous. With this patch applied, no more aliases are defined this way. Fixes: systemd#1169 Fixes: systemd#5179 Fixes: systemd#4830
This patch allows masking of .wants symlinks in /usr/lib by /dev/null symlinks in /etc. IOW it gives the possibility to mask a dependency symlink (in .wants or .requires), assuming that the later being located in a directory having a 'lower' precedence. As a consequence a dependency symlink mask in /etc/ will mask all equivalent dependency symlinks located in directories such as /run, /usr/lib. And a dependency symlink mask in /usr/lib will has no effect on a dropin symlink located in /run or /etc. Consider the following example: /etc/systemd/system/foo.service.wants/bar.service -> /dev/null /usr/lib/systemd/system/foo.service.wants/bar.service -> ../bar.service service 'foo' was installed during a package installation with a dependency on 'bar' and later sysadmin decided to disable this dependency by adding a mask in /etc/. This results in service 'bar' not being pulled in by 'bar' anymore. The primary use case is to allow a clean separation between service activation/deactivation done during package installations/updates and those done by sysadmin. Assuming that distro dependency symlinks happens in /usr/lib only, the sysadmin is now able to disable persistently a service by creating a mask in /etc/. This mask will take precedence over any distro policies (usually defined by presets) defined in /usr/lib. The distro policies (presets) are now free to be changed without interfering with the configuration done by the sysadmin (if any). They will still be preserved and will still be taken into account. The assumption on distros creating symlinks in /usr/lib only is currently not met since this happens in /etc/. However the dropin symlink mask is the first step to achieve this clean separation. Later changes will ensure that dropin symlinks created during package installations/updates will happen in /usr/lib only. See issue systemd#4830. In order to implement the masking of dependency symlinks properly, this patch defers the handling of those dependencies until all involved units are fully loaded. This way all units and especially their aliases are known and a mask can be effective on a specific unit including all of its aliases. This is due to the way we currently handle the unit aliases (how a unit and its aliases are "lazily" merged) and also due to the fact that we can't remove dependencies added to a unit (until a full daemon-reload happens). As an example: /etc/.../a.service.wants/b1.service -> /dev/null /usr/.../a.service.wants/b.service -> ../b.service and 'b1' is an alias of 'b'. In this example, dropin symlink dependencies pulling 'b' or any of its aliases (including 'b1') will be masked. This patch also changes a rather odd behavior that nobody sane should rely on: it's possible to define an alias for unit 'c' via a dependency symlink: /etc/.../a.service.wants/alias-for-c.service -> ../c.service In this case 'alias-for-c' is an alias of 'c' assuming that 'alias-for-c' unit doesn't exist. I'm not sure why symlinks were used to define dependencies in .wants/.requires in the first place whereas a simple empty file would have been sufficient and unambiguous. With this patch applied, no more aliases are defined this way. Fixes: systemd#1169 Fixes: systemd#5179 Fixes: systemd#4830
…conf dropins Essentially, instead of sequentially adding deps based on all symlinks encountered in .wants and .requires dirs for each name and each unit file load path, iteratate over the load paths and unit names gathering symlinks, then order them based on priority, filter our masked entries, and then iterate over the final list, adding dependencies. This patch doesn't change the logic too much, except of course the added ability to mask entires in .wants and .requires. Adding additional filtering on the symlinks is left for later patches. Fixes systemd#1169. Fixes systemd#4830. Example log errors: Feb 04 22:13:28 systemd[1462]: foo.service: Wants dependency on empty_file.service is masked by /home/zbyszek/.config/systemd/user/foo.service.wants/empty_file.service, ignoring Feb 04 22:13:28 systemd[1462]: foo.service: Wants dependency on masked.service is masked by /home/zbyszek/.config/systemd/user/foo.service.wants/masked.service, ignoring
Alt version: #5231. |
…conf dropins Essentially, instead of sequentially adding deps based on all symlinks encountered in .wants and .requires dirs for each name and each unit file load path, iteratate over the load paths and unit names gathering symlinks, then order them based on priority, filter our masked entries, and then iterate over the final list, adding dependencies. This patch doesn't change the logic too much, except of course the added ability to mask entires in .wants and .requires. Adding additional filtering on the symlinks is left for later patches. Fixes #1169. Fixes #4830. Example log errors: Feb 04 22:13:28 systemd[1462]: foo.service: Wants dependency on empty_file.service is masked by /home/zbyszek/.config/systemd/user/foo.service.wants/empty_file.service, ignoring Feb 04 22:13:28 systemd[1462]: foo.service: Wants dependency on masked.service is masked by /home/zbyszek/.config/systemd/user/foo.service.wants/masked.service, ignoring
@poettering : I don't think this one can be closed yet. Some pieces are still missing before distros can fully create symlinks in /usr/lib/systemd/*, see #4830 (comment) We also still need to adapt |
Hi,
This issue is not really new and there were some attempts to address it in the past already but unfortunately the work got lost somehow.
For example the latest work I found is the preset-transient one which is in my understanding about having symlinks created by the preset commands in /run/systemd/ thus having lower precedence than the symlinks created by the sysadmin in /etc/systemd/. However a part is still missing as disabling services should create null symlinks now in /etc/ and should override any corresponding symlinks directories with lower precedences.
There might another possibility though: basically when the user is enabling/disabling a service (via the EnableUnitFiles bus call), this operation would be recorded via a dedicated preset directive in a preset file located for example in /etc/systemd/system-preset/50-user-preferences.preset
For example if the user does
systemctl enable foo.service
, this operation would add "enable foo.service" in the dedicated preset file. A subsequentsystemctl disable foo
would replace the previous directive with "disable foo.service".Since the preset file is located in /etc/systemd/systemd-preset, running
systemd preset foo.service
command would simply restore the user last preferences and would ignore the distro defaults as they arenow overridden.
However this wouldn't work if the sysadmin created the symlinks manually (using ln(1)) but in this case it might be acceptable to consider that creating symlinks in this way is considered as low-level operations and thus might be missing some functionalities (but of course its main purpose would still work).
The text was updated successfully, but these errors were encountered: