/var/lib/gdm/.config/ equivalent?

Reading the Arch wiki on how to make GDM set the resolution and refresh rate properly it says:

If you have your monitors setup as you like (resolution, refresh rate, orientation, scaling, primary and so on) in ~/.config/monitors.xml and want GDM to honor those settings:

cp ~/.config/monitors.xml /var/lib/gdm/.config/

I’ve been searching a bit here, but can’t really see anything related.

What would be the proper way to do this in NixOS?

I don’t think there’s a convenient NixOS option for this yet; I’ve managed it by adding a systemd.tmpfiles.rules entry:

"L /run/gdm/.config/monitors.xml - - - - ${gdmMonitors}"

where gdmMonitors is a file created with writeText.

1 Like

oof that one went right over my head :sweat_smile:

A learning moment, if I may - is it so that whoever is responsible for the GDM package must make the options for something like this? Like, they would have to make something like an option services.xserver.displayManager.gdm.monitorXML where this could be put?

Not necessarily! Modules will generally have their own maintainers. You can see the module associated with a NixOS option by clicking the ‘Declared in’ link, and in that module there is often a meta.maintainers entry.

In this case, though, it’s teams.gnome.members that are maintainers for both pkgs/desktops/gnome/core/gdm/default.nix the package and nixos/modules/services/x11/display-managers/gdm.nix the NixOS module.

But more importantly, anyone can submit a PR with a new option, not just the maintainers. You can see in the module that there are already a few conditional systemd.tmpfiles.rules entries added if certain options are enabled; you’d just be adding another one of those.

2 Likes

The difference is just because of a different home directory:

$ grep gdm /etc/passwd
gdm:x:132:132:GDM user:/run/gdm:/run/current-system/sw/bin/nologin

I do not see the need for a separate option when systemd.tmpfiles.rules is the standard option for creating files.

See nixos/gdm: add option to configure monitors.xml by isobit · Pull Request #107850 · NixOS/nixpkgs · GitHub for a previous PR.

2 Likes

One thing that is easy to get wrong w.r.t. modules is that your configuration.nix is also a module just like the GDM module and can therefore also do anything that an upstream NixOS module could do. There’s nothing special about the modules in NixOS

This doesn’t need to be an upstream module, you can simply set it in your config. You could even make your own options in your config.

It’d be great to have this monitorXML option upstream though. Feel free to send a PR to Nixpkgs for that if you’re motivated; anyone can contribute.

All the more reason to have a dedicated NixOS option IMHO.

I spent quite a bit of time trying to configure GDM via /etc/gdm/ once and it just would not work. I had assumed GDM was buggy but TIL it’s actually a config quirk on our side.

A NixOS option would greatly help abstract such details.

Having to resort to home-manager to configure a system display-manager does not strike me as a sane solution.

Ok, so I think I am understanding this a bit more.
The path from the Arch doc is just the config dir under the home directory of GDM. So I just need to put the file in GDM’s home in NixOS, which is /run/gdm. And this can be done by:

  1. Writing a file with writeText
  2. Symlink that file to GDM’s ~/.config/monitors.xml with systemd.tmpfiles.rules

Trying to make this work, but I’m struggling a bit. I’ve lost track of all the different ways I’ve tried up until now. This is where I am at at the moment, from configuration.nix:

    monitorsxml = writeText {
      name = "monitorsxml";
      text = ''
(a lot of monitor text)
  '';
    };

    systemd.tmpfiles.rules = [
      "L /run/gdm/.config/monitors.xml - - - - ${monitorsxml}"
    ];

I am getting error: undefined variable 'monitorsxml'.
I’m sure it’s trivial… can anyone see my mistake?

Your understanding here is correct, the problem is with your Nix syntax.

You are probably defining monitorsxml as a NixOS option, which does not actually exist. Nor will it set a Nix variable your later option expects, causing the evaluation error.

The most common method of creating “variables” is a let expression. You would either use it at the top-level, so that the configuration attribute set is in the let … in body:

{ pkgs, lib, ... }:

let
  monitorsxml = /* … */;
in
{
  # …

  systemd.tmpfiles.rules = [
    "L /run/gdm/.config/monitors.xml - - - - ${monitorsxml}"
  ];
}

or, what is probably cleaner, narrowing the scope by defining it closer:

{ pkgs, lib, ... }:

{
  # …

  systemd.tmpfiles.rules =
    let
      monitorsxml = /* … */;
    in
    [
      "L /run/gdm/.config/monitors.xml - - - - ${monitorsxml}"
    ];
}
2 Likes

aha! let ... in of course.
I have tried both top-level and scoped down, but it doesn’t go through:

nixos-rebuild switch 
building Nix...
building the system configuration...
error:
       … while calling the 'head' builtin

         at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/attrsets.nix:1575:11:

         1574|         || pred here (elemAt values 1) (head values) then
         1575|           head values
             |           ^
         1576|         else

       … while evaluating the attribute 'value'

         at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:809:9:

          808|     in warnDeprecation opt //
          809|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
             |         ^
          810|         inherit (res.defsFinal') highestPrio;

       (stack trace truncated; use '--show-trace' to show the full trace)

       error: cannot coerce a function to a string

I tried looking through both those files, but I have no idea what it means. The error must be from this:

    systemd.tmpfiles.rules = [
      "L /run/gdm/.config/monitors.xml - - - - ${monitorsxml}"
    ];

since without it builds just fine. And I can’t see where I am supposedly trying to “coerce a function to a string” :thinking:

Check the manual again: writeText wants to be applied to two strings, not an attribute set. (writeTextFile is the one that wants an attribute set. You can use either here, as long as you give whichever one you use the thing that it wants.)

This results in cannot coerce a function to a string because the result of applying writeText to one argument is a function—the function that would be applied to a second argument if there was one. Interpolating that function into the string literal in systemd.tmpfiles.rules causes the error.

2 Likes

That was it, thanks for the help rhendric and everyone!

If anyone is to stumble upon this in the future - this is how to create a file with content and symlink it to whereever you want:

{ config, pkgs, ... }:
{
    systemd.tmpfiles.rules =
    let
      monitors.xml = pkgs.writeText 
        "monitors.xml"
        ''
          FILE CONTENT GOES HERE
        '';      
    in
    [
      "L /run/gdm/.config/monitors.xml - - - - ${monitors.xml}"
    ];

}

Note that this will create an monitors attribute set with a single xml attribute (same as { monitors = { xml = ...; }; }). You may want to use monitorsXML instead.

2 Likes

The default behaviour for the user gdm on nixos is to have a temporary home directory at /run/gdm. I suspect that we can override the default and appoint for a static directory. Try repl on system flake, and inspect the property

nixosConfigurations.YourHostName.config.users.users.gdm

You can see properties like

{
  autoSubUidGidRange = false;
  createHome = false;
  cryptHomeLuks = null;
  description = "GDM user";
  expires = null;
  extraGroups = [ ... ];
  group = "gdm";
  hashedPassword = null;
  hashedPasswordFile = null;
  home = "/run/gdm";
  homeMode = "700";
  ignoreShellProgramCheck = false;
  initialHashedPassword = null;
  initialPassword = null;
  isNormalUser = false;
  isSystemUser = false;
  linger = false;
  name = "gdm";
  openssh = { ... };
  packages = [ ... ];
  pamMount = { ... };
  password = null;
  passwordFile = null;
«derivation /nix/store/2zgfnqgn20abs8rwj9hg1i91v150xl4l-shadow-4.16.0.drv»;
  subGidRanges = [ ... ];
  subUidRanges = [ ... ];
  uid = 132;
  useDefaultShell = false;
}

What about set the home directory to some stable location?

I think the transient home directory is intentional since it was set by us from the start.

I would also say it is preferable since less persistent state means fewer problems, allowing Nix to actually shine. It is similar, in effect, to impermanence, except safe to enable by default since people are not typically expected to modify stuff in gdm’s home directory.

The main thing to improve here are docs.