Systemd oneshot, binary not found?

Every time I boot up I run this command:
sudo nvidia-smi --lock-memory-clocks=5000
If I don’t the mouse cursor lags slightly for a fraction of a second when I move the mouse after it’s been idle for a few seconds and it drives me crazy.

Anyway!

I figured it was time to make it into a service, but I’m struggling a bit. This is what I have:

systemd.services = {
    "nvidia-lock-mem-clock" = {
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        Type = "oneshot";
        ExecStart = "nvidia-smi --lock-memory-clocks=5000";
      };
    };
  };

but it gives the following error:

Starting nvidia-lock-mem-clock.service...
nvidia-lock-mem-clock.service: Unable to locate executable 'nvidia-smi': No such file or directory
nvidia-lock-mem-clock.service: Failed at step EXEC spawning nvidia-smi: No such file or directory
nvidia-lock-mem-clock.service: Main process exited, code=exited, status=203/EXEC
nvidia-lock-mem-clock.service: Failed with result 'exit-code'.
Failed to start nvidia-lock-mem-clock.service.

I’m sure this is trivial, but I can’t figure it out. I’m guessing I need to put ${hardware.nvidia.package}/bin/nvidia-smi or something like that somewhere, but Nix ain’t having my tries so far.

systemd services don’t inherit envvars (by default) by design, you have to provide the full paths. And it should be config.hardware... if you use that package

Alright! One step closer! config.hardw... helped, thanks.
Still not quite there, though.

${config.hardware.nvidia.package} resolves to:
/nix/store/0glr85066x0lpj4c7xmzk4dqwwi6p338-nvidia-x11-560.35.03-6.6.63
whereas nvidia-smi is located in:
/nix/store/yxvfycswbqg3afs66ghys2wxp5b7mw9f-nvidia-x11-560.35.03-6.6.63-bin/

-bin vs no--bin in the folder name. So a different package/option/variable, I guess? Maybe something the driver derivation thing pulls in?

This is my graphics driver - I have no idea where it is being pulled from or how any of this works; I copied it from some genius on the web:

nvidia.package = config.boot.kernelPackages.nvidiaPackages.mkDriver {
      version = "560.35.03";
      sha256_64bit = "sha256-8pMskvrdQ8WyNBvkU/xPc/CtcYXCa7ekP73oGuKfH+M=";
      sha256_aarch64 = "sha256-s8ZAVKvRNXpjxRYqM3E5oss5FdqW+tv1qQC2pDjfG+s=";
      openSha256 = "sha256-/32Zf0dKrofTmPZ3Ratw4vDM7B+OgpC4p7s+RHUjCrg=";
      settingsSha256 = "sha256-kQsvDgnxis9ANFmwIwB7HX5MkIAcpEEAHc8IBOLdXvk=";
      persistencedSha256 = "sha256-E2J2wYYyRu7Kc3MMZz/8ZIemcZg68rkzvqEwFAL3fFs=";
    };

Maybe I can just ask a whole lot simpler:
How can I reference this executable in my configuration.nix:

[marius@gorno:~]$ ls -la $(which nvidia-smi)
lrwxrwxrwx 1 root root 90 jan.   1  1970 /run/current-system/sw/bin/nvidia-smi -> /nix/store/yxvfycswbqg3afs66ghys2wxp5b7mw9f-nvidia-x11-560.35.03-6.6.63-bin/bin/nvidia-smi

Do I really have to hardcore the path?
Edit: I guess /run/current-system/sw/bin/nvidia-smi is ok to hardcore as it should just swap out with new versions :thinking:

inb4: not on Nvidia, so cannot comment on the validity of nvidia config itself.

${config.hardware.nvidia.package}

You’re looking for ${config.hardware.nvidia.package.bin}/bin/nvidia-smi (note the “bin” attribute). If you search for nvidia-x11 (which is the package name) in NixOS packages, you can see that it has multiple outputs, including the “bin” one.

While technically this will work, it creates an implicit dependency on the existence of that path and the systemd unit having access to that path.

I am not sure how this guarantee would hold up in the long run, so (personal preference) I try to use the paths to the binary using the package reference.

1 Like

Thank you, it works perfectly with the proper… “attribute”(?)! Feels much more proper without the hardcoding.

I was about to ask how you went about the detective work but I think I am about to give up understanding how NixOS is put together. And the syntax is just so hard :weary:

Anyway, thanks again! :partying_face:

Edit: ok, let me just ask this one last thing.
Is this where you found the outputs?

outputs = [ "out" ]
      ++ lib.optional i686bundled "lib32"
      ++ lib.optional (!libsOnly) "bin"
      ++ lib.optional (!libsOnly && firmware) "firmware";
    outputDev = if libsOnly then null else "bin";

If so, how did you get config.hardware.nvidia.package.bin from that?

If you look at what the nvidia module adds to environment.sytemPackages it’s pretty obvious: nixpkgs/nixos/modules/hardware/video/nvidia.nix at 394571358ce82dff7411395829aa6a3aad45b907 · NixOS/nixpkgs · GitHub

Don’t! A lot of the time reading the source code is the only way to actually figure stuff out at the moment. Getting to grips with reading nix is how you transition from just trying stuff to actually understanding.

Once you get the hang of the difference between packages and modules, and roughly know how to read and write them, things end up looking a lot clearer.

The nvidia stuff is also some of the most complex code you’ll see, so don’t be discouraged by not grokking it at a glance.

The bin output from that list is added as an attribute to the package. That’s just how stdenv.mkDerivation works, and a detail you have to know.

How @VTimofeenko understood that the binary would be in there from just that, no idea, probably just an educated guess. Looking at the module gives a more direct link.

Edit: derp, you already figured out that it is in the bin output, so that’s a no-brainer, @waffle8946 is correct.

1 Like

If you see the store path as -bin, -dev, etc. those are common outputs.

The format of store paths is /nix/store/<hash>-<name> and name is generally <pname>-<version> or <pname>-<version>-<output> in nixpkgs.

I assume from OP mentioning the full store path earlier

2 Likes

I assume from OP mentioning the full store path earlier

Correct. I call it the “intense staring” method. Works with nix quite often :slight_smile:

1 Like