Nixpkgs: xlock missing after enabling xautolock

Created on 6 Aug 2019  Â·  15Comments  Â·  Source: NixOS/nixpkgs

Describe the bug
The services.xserver.xautolock.locker default is "${pkgs.xlockmore}/bin/xlock", but that's not installed by default.

To Reproduce
Steps to reproduce the behavior:

  1. Set services.xserver.xautolock.enable = true;
  2. sudo nixos-rebuild switch

Expected behavior
At this point I should have a working xautolock installation.

Actual behavior
The screen goes black but doesn't lock after services.xserver.xautolock.time, presumably because of this:

$ xlock
The program ‘xlock’ is currently not installed. You can install it by typing:
  nix-env -iA nixos.xlockmore

Additional context

Some ways this could be fixed:

  • Exit with an error if services.xserver.xautolock.locker (or its default value) points to a non-existing path.
  • Install xlockmore if services.xserver.xautolock.locker is not set.
  • Make services.xserver.xautolock.locker mandatory and not defaulted.

Metadata

$ nix run nixpkgs.nix-info --command nix-info --markdown
 - system: `"x86_64-linux"`
 - host os: `Linux 4.19.61, NixOS, 19.03.173198.aeeb30a3b29 (Koi)`
 - multi-user?: `yes`
 - sandbox: `yes`
 - version: `nix-env (Nix) 2.2.2`
 - channels(root): `"nixos-19.03.173198.aeeb30a3b29"`
 - channels(username): `""`
 - nixpkgs: `/nix/var/nix/profiles/per-user/root/channels/nixos`

Maintainer information:

attribute:
  - services.xserver.xautolock.enable
  - services.xserver.xautolock.locker
bug

Most helpful comment

After getting a :+1: from @l0b0 on #66561 and no objections for almost a week I guess that it should be safe to close this now.

All 15 comments

The module is not supposed to make xlock available in your environment, it merely uses it by default to lock your screen.
If you need xlock to be available in your environment you could for example add it declaratively via environment.systemPackages or users.users.<name>.packages.

Alternatively you could specify services.xserver.xautolock.nowlocker and use xautolock -locknow in your shell to lock your screen on demand.

Excuse me for replacing the label, I was focused on the statement that xlock is not in your environment and overlooked the actual issue.

Actual behavior
The screen goes black but doesn't lock after services.xserver.xautolock.time, presumably because of this:

As stated before it is normal that xlock is not available in your environment even though xautolock is enabled, but this looks like an issue with xlock itself. Could you provide some log output to debug the cause of the failing xlock?
The log should be available via journalctl --user -eu xautolock.service.

~Additionally can you please tell us on which rev of nixpkgs you tested this?~

I just created the following VM declaration on master (fbc02cd1869893faf97b8c54f678c0e0d56aba96) and release-19.03 (314e2acfe83827b0946492aa620d8e37f0f29602):

{
  xautolock = { pkgs, ... }:
    { imports = [ ./nixos/tests/common/x11.nix ./nixos/tests/common/user-account.nix ];
      services.xserver.displayManager.auto.user = "bob";

      services.xserver.xautolock = {
        enable = true;
        time = 1;
      };
    };
}

Without xlock installed directly it still works perfectly fine as the store path is used in the systemd unit, not something from $PATH.

@WilliButz I don't understand your question. xlock isn't failing, it just wasn't installed. And I consider that a bug, because it means that what xautolock is used for (locking the screen) does not work out of the box.

@Ma27 Which "it" works perfectly? Does it install xlock so that locking the screen works?

I don't understand your question. xlock isn't failing, it just wasn't installed. And I consider that a bug, because it means that what xautolock is used for (locking the screen) _does not work out of the box._

In the default module configuration ${pkgs.xlockmore}/bin/xlock is the default value for locker. This does not "install" xlock on the system as one might expect on other distributions.

In the service file for xautolock the part in braces is replaced by the output path of xlockmore in the nix store (something like /nix/store/<hash>-xlockmore-<version>). This means xautolock directly calls the binary xlock by using its canonical path /nix/store/<hash>-xlockmore-<version>/bin/xlock.

When you enter xlock in your shell and xlock is not found in any of the directories specified in your PATH environment variable and also not defined as a function, you get the error you posted in the first comment. This is perfectly normal when you did not specifically add it to your environment and does not interfere with xautolocks functionality.

Can you please confirm that the xautolock service is running and if so, post any errors you get from its log, using:

systemctl --user status xautolock.service

and

journalctl --user -eu xautolock.service

I can also confirm what @ma27 reported and am unable to reproduce any failure of xautolock or xlock itself on release-19.03 and master.

@WilliButz xautolock runs fine after installing xlockmore; the only error in systemctl --user status xautolock.service is

xlock: can not find font: *8x16*, using fixed...

which I don't know how to fix yet (I've installed terminus_font which claims to support 8x16, but it's not used for some reason).

That error is of course also in journalctl --user -eu xautolock.service, and there's also a few

xautolock is already running (PID [number]).

entries which I don't know the source of.

I'm not sure why you're asking about these though; the problem AFAICT is simply that enabling xautolock is in itself not enough to have a functioning locker. I need to somehow realize that

  1. I need to install a locker (not easy to derive from just the fact that the screen goes black after 15 minutes but doesn't lock) and
  2. if that locker is not xlockmore I will need to configure it (also not easy to derive unless you read the Nix module).

I expect for many users (or 100% of non-programmers) the distinction between an "idle detector" like xautolock (which even has "lock" in the name) and a "locker" like xlockmore is about as academic as the difference between a desktop manager and a window manager, and that it would be helpful (as in, not necessarily theoretically or academically correct, but something which could possibly help adoption) to install the missing link when enabling xautolock but not specifying a custom locker command.

Would it for example be possible for the resolution of ${pkgs.xlockmore} to fail if xlockmore is not installed? Is there a way to express this sort of relationship in an expression, to get a useful error message if the locker command does not exist because the required package is not installed?

@Ma27 Which "it" works perfectly? Does it install xlock so that locking the screen works?

The VM with the configuration I posted above worked as I'd expect it: after a configured time of inactivity, xlock is started and locks the screen, although it's not "installed" a.k.a. not linked into a directory which is in $PATH. The correct behavior is also confirmed in nixos/tests/xautolock.nix.

xautolock runs fine after installing xlockmore; the only error in systemctl --user status xautolock.service is

As @WilliButz tried to explain yesterday, this is actually not necessary. Nix evaluates ${pkgs.xlockmore} to the appropriate store path. Also Nix is smart enough to "detect" those references in expressions and ensures that the store path exists on your machine by either building pkgs.xlockmore or fetching it from a binary cache.

That error is of course also in journalctl --user -eu xautolock.service, and there's also a few

I understand that you are currently experiencing an issue that is pretty frustrating. However I'm afraid that we can't precisely reason about what's happening in your setup without having the complete logs of the unit.

I'm not sure why you're asking about these though; the problem AFAICT is simply that enabling xautolock is in itself not enough to have a functioning locker. I need to somehow realize that

It's not that easy though: when activating a new configuration, nixos-rebuild runs a script named switch-to-configuration.pl. This script detects newly activated or changed systemd units and reloads or restarts them. However this doesn't apply to user units such as xautolock.service.

What I'm trying to explain is that after the activation of the new system configuration it's not guaranteed by NixOS that the unit is already started.

I need to install a locker (not easy to derive from just the fact that the screen goes black after 15 minutes but doesn't lock) and

Admittedly I'm still not entirely convinced that this actually true. You shouldn't need to install a new package if you already reference the store path. Additionally everything that's not a store path only works in a systemd unit when it's referenced in the unit's path variable. For instance, the following unit would break:

{
  systemd.user.services.mylock = {
    wantedBy = [ "graphical-session.target" ];
    partOf = [ "graphical-session.target" ];
    script = ''
      xsecurelock
    '';
  };
}

When running journalctl --user -u mylock, you'd get an error like this:

Aug 07 08:53:30 hauptschuhle systemd[2666]: Started mylock.service.
Aug 07 08:53:30 hauptschuhle 9pps06jlz4yh2l7wxzgpbjq6nipr4ypz-unit-script-mylock-start[20832]: /nix/store/9pps06jlz4yh2l7wxzgpbjq6nipr4ypz-unit-script-mylock-start: line 2: xsecurelock: command not found
Aug 07 08:53:30 hauptschuhle systemd[2666]: mylock.service: Main process exited, code=exited, status=127/n/a
Aug 07 08:53:30 hauptschuhle systemd[2666]: mylock.service: Failed with result 'exit-code'.

Also, xsecurelock is installed on my system:

$ which xsecurelock
/run/current-system/sw/bin/xsecurelock

if that locker is not xlockmore I will need to configure it (also not easy to derive unless you read the Nix module).

May I ask what you'd propose to improve that? In the end it's sufficient to reference any kind of executable (preferably a store path) like ${pkgs.mylocker}/bin/mylocker which is a pretty common pattern when working with Nix.

@WilliButz xautolock runs fine after installing xlockmore; the only error in systemctl --user status xautolock.service is

I reckon that between the time you originally experienced the issue that xautolock does not lock after the specified time and the time you added xlockmore to your environment, the user service was actually started (maybe by a reboot or during debugging).
To make sure this is the case, you can remove xlockmore from your environment.systemPackages again and rebuild your system which should leave the functionality of xautolock unimpaired.

I'm not sure why you're asking about these though; the problem AFAICT is simply that enabling xautolock is in itself not enough to have a functioning locker. I need to somehow realize that

This might be caused by what @ma27 mentioned above, that simple running a rebuild of your system is not sufficient to get the user service started. That would be unfortunate and needs to be looked into.

  1. I need to install a locker (not easy to derive from just the fact that the screen goes black after 15 minutes but doesn't lock) and

You don't need to manually install the locker, because Nix takes care of placing it in the nix store when the package attribute in your system configuration is evaluated successfully.

  1. if that locker is not xlockmore I will need to configure it (also not easy to derive unless you read the Nix module).

That is the case for every other distribution one would use xautolock on, because xautolock needs to be configured to use special lockers.

I expect for many users (or 100% of non-programmers) the distinction between an "idle detector" like xautolock (which even has "lock" in the name) and a "locker" like xlockmore is about as academic as the difference between a desktop manager and a window manager, and that it would be helpful (as in, not necessarily theoretically or academically correct, but something which could possibly help adoption) to install the missing link when enabling xautolock but not specifying a custom locker command.

This seems to drift off from a bit from the actual issue. I understand that this was an unpleasant experience but I also think that a user should not blindly activate services/modules or install packages without first informing themselves about their purpose or usage. In doing so they would be able to make that distinction quite easily.

Would it for example be possible for the resolution of ${pkgs.xlockmore} to fail if xlockmore is not installed? Is there a way to express this sort of relationship in an expression, to get a useful error message if the locker command does not exist because the required package is not installed?

The package is still not installed as known from other distributions/package managers.
In simplified terms the above expression in the xautolock module configuration causes nix to evaluate the attribute pkgs.xlockmore.
Assuming there is no such attribute or the evaluation would fail for some other reason, the evaluation of your whole system would also fail and therefore a rebuild with a broken package definition would not be possible.
Nix detects that your system depends on the pkgs.xlockmore attribute and therefore places its output, which includes the binary xlock, in the nix store. This is sufficient and there is no change to any user environment needed.

@WilliButz xautolock runs fine after installing xlockmore; the only error in systemctl --user status xautolock.service is

I reckon that between the time you originally experienced the issue that xautolock does not lock after the specified time and the time you added xlockmore to your environment, the user service was actually started (maybe by a reboot or during debugging).
To make sure this is the case, you can remove xlockmore from your environment.systemPackages again and rebuild your system which should leave the functionality of xautolock unimpaired.

You're right - removing xlockmore and rebuilding leaves xautolock running.

I'm not sure why you're asking about these though; the problem AFAICT is simply that enabling xautolock is in itself not enough to have a functioning locker. I need to somehow realize that

This might be caused by what @Ma27 mentioned above, that simple running a rebuild of your system is not sufficient to get the user service started. That would be unfortunate and needs to be looked into.

I believe this is the issue. To reproduce:

  1. Change service.xserver.xautolock.time (in my case from being absent to 1)
  2. Run sudo nixos-rebuild switch successfully

At this point I would expect journalctl --user-unit xautolock.service to show a new set of

Stopping xautolock service...
Stopped xautolock service.
Started xautolock service.

lines, but it does not. I have to manually systemctl --user restart xautolock.service. On a related note there are a few instances of "xautolock.service: Current command vanished from the unit file, execution of the command list won't be resumed." in the journal - presumably this is caused by the xautolock executable being relocated to a different hash because of some Nix internals I don't understand.

  1. I need to install a locker (not easy to derive from just the fact that the screen goes black after 15 minutes but doesn't lock) and

You don't need to manually install the locker, because Nix takes care of placing it in the nix store when the package attribute in your system configuration is evaluated successfully.

You're absolutely right, I did not realize because

  1. xautolock didn't lock yet and
  2. xlock is not installed in a $PATH directory.

Would it for example be possible for the resolution of ${pkgs.xlockmore} to fail if xlockmore is not installed? Is there a way to express this sort of relationship in an expression, to get a useful error message if the locker command does not exist because the required package is not installed?

The package is still not installed as known from other distributions/package managers.
In simplified terms the above expression in the xautolock module configuration causes nix to evaluate the attribute pkgs.xlockmore.
Assuming there is no such attribute or the evaluation would fail for some other reason, the evaluation of your whole system would also fail and therefore a rebuild with a broken package definition would not be possible.
Nix detects that your system depends on the pkgs.xlockmore attribute and therefore places its output, which includes the binary xlock, in the nix store. This is sufficient and there is no change to any user environment needed.

Absolutely right, again. After both of your (plural) explanations I finally realized I was running right into an X-Y problem. Thank you so much for taking the time to explain in detail. It'll still take me a while to get used to how Nix works, but this is brilliant.

By the way, this is such a groundbreaking distro and friendly community that I decided to contribute via Open Collective as of today. Thank you!

Regarding the user service issue, this seems to be a somewhat known problem (e.g. #65462) and needs to be fixed in general.

Absolutely right, again. After both of your (plural) explanations I finally realized I was running right into an X-Y problem. Thank you so much for taking the time to explain in detail. It'll still take me a while to get used to how Nix works, but this is brilliant.
By the way, this is such a groundbreaking distro and friendly community that I decided to contribute via Open Collective as of today. Thank you!

Very glad to read that :+1:

I have some additional pointers for you to get to know Nix better, in case you didn't already know about them:

And of course the nixos* IRC channels, mainly #nixos on freenode.

Don't forget Discourse :)
I like it a lot better than IRC because you can have persistent threads.

First of all, I'm glad that we could help you and found the cause of your issue!

Regarding the user-services issue: this is indeed a general issue that can't be simply fixed as the way how these services are structured slightly differs from the way how systemd manages system-wide services (at least about a year ago when I had a closer look at the problem), so this would be definitely a non-trivial change.

As we found the actual issue here, I guess that we can close this @l0b0, right?

@Ma27 If user services don't get started or stopped properly when switching generation that seems like a major issue. Not so much because user services are likely to be absolutely critical (which I expect is rare) but because it means a whole class of changes to the configuration are broken, it's not obvious why it's happening, and it requires non-trivial user intervention to work around. Could this be documented so prominently that it would be almost impossible to miss? I would have certainly appreciated it if every option which starts a user service linked to a quick explanation and workaround.

Not so much because user services are likely to be absolutely critical (which I expect is rare) but because it means a whole class of changes to the configuration are broken, it's not obvious why it's happening, and it requires non-trivial user intervention to work around. Could this be documented so prominently that it would be almost impossible to miss? I would have certainly appreciated it if every option which starts a user service linked to a quick explanation and workaround.

After grepping over the documentation I realized that this is indeed not sufficiently documented. The actual problem should be tackled in a different issue, however I'd prepare a PR tomorrow which adds some documentation about this in the manual and add you as reviewer :)

After getting a :+1: from @l0b0 on #66561 and no objections for almost a week I guess that it should be safe to close this now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vaibhavsagar picture vaibhavsagar  Â·  3Comments

teto picture teto  Â·  3Comments

domenkozar picture domenkozar  Â·  3Comments

ghost picture ghost  Â·  3Comments

matthiasbeyer picture matthiasbeyer  Â·  3Comments