2022-08-05 Building a crappy cheap LightDM 1/?

This is a lightly edited mind dump, don't expect a well written post here.

What it is, does and why I built it

The goal of this little project was to build a collection of tools that is able to switch between two sessions on a Linux system for the purpose of having a session for working and one for logging in and unlocking the screen, the way it is implemented is by changing virtual terminals to the corresponding session when certain state-changes occur, much like LightDM and the light-locker.

The reason I'm doing this is because I want to be able to replace my currently pretty barebones screenlockers with a rich screenlocker that is able to do more than just showing me an unlock form, mostly for my phone but also for my other machines.

Why not just use LightDM?

Maybe oversimplified answer: I have the opinion that LightDM isn't great on mobile.

Implementation

The core of this contraption are two instances of greetd, one for actually logging in and one for giving me a properly set up session to run my compositor in which could be replaced by an instance of tinydm on my phone. These two communicate by always running one of two scripts the locker and the unlocker, both grab the current vt with the chvt command, write to pidfiles and kill each other and run hooks on exit.

Please Note: For my current implementation the user that runs the logged in session needs passwordless sudo access to the chvt command, in a multiuser scenario this may not be the best idea.

Unlocking

Unlocking works by entering your credentials in the greeter, triggering the unlock script which sends a SIGUSR1 to the locker process (using the pidfile), and then entering a sleep loop waiting for the next session lock.

The lock script handles this SIGUSR1 by killing the lockscreen (in my case swaylock) and using chvt to switch the currently active terminal to the one of the real-work-session.

Locking

Locking works almost exactly the reverse way as unlocking. The lock script starts the screenlocker, kills the unlocker, this time with an ordinary SIGKILL and starts waiting for being unlocked.

The unlocker uses chvt to grab the vt with the greeter on it and exits telling greetd to start the greeter session for the next unlock.

Bootstrapping

To solve our little problem of how to bootstrap this thing there is also an initial-session-lock, a script that writes its pid to the lockers pidfile and only exits with a success code when it receives a SIGUSR1 to act as a trigger for starting the session on the first unlock. For shutting down my current solution is to wrap this in a loop and kill the unlocker after sway exits.

Code

Currently this is an experiment I've run on my personal dotfiles so the sources are not really in any central location yet and you probably don't want to just copypasta my scripts.

Scripts

Greetd configuration

As mentioned above I have two instances of greetd running, one that starts the unlocker on login and one that starts the actual session.

A pretty standard greetd configuration with the unlocker script instead of a real session.
[terminal]
# The VT to run the greeter on. Can be "next", "current" or a number
# designating the VT.
vt = 7

# The default session, also known as the greeter.
[default_session]

# Starting tuigreet here which starts the unlocker command directly to unlock the second session
command = "tuigreet --cmd unlocker -r -t"

# The user to run the command as. The privileges this user must have depends
# on the greeter. A graphical greeter may for example require the user to be
# in the `video` group.
user = "_greeter"

The one starting the actual session could be replaced by something like tinydm as I only care about the autostarting part and that the environment is properly set up so I don't have to do it myself here.

A greetd configuration that uses the `initial_session` mechanism to autostart a shell oneliner that starts our session.
[terminal]
# The VT to run the greeter on. Can be "next", "current" or a number
# designating the VT.
vt = 8

[general]

# Don't know if actually necessary but I don't want to restart the session I'm writing this post in
runfile = "/run/slatian-greetd-initial-session"

[initial_session]

# The command that wits for the initial unlock, if successful starts the session and returns the control to the lockscreen after the session ends before it starts over.
command = "sh -c 'while true; do initial-session-lock && dbus-run-session sway; kill \"$(head -n 1 \"$XDG_RUNTIME_DIR/unlocker.pid\")\"; done'"
user = "slatian"

# default session omitted … nothing interesting there

Sudo configuration

The following snippet placed in a file in /etc/sudoers.d/ will give everyone in the wheel group (the group that has sudo access anyways, may also be called sudo on some systems) the permission to run chvt without having to enter a password.

%wheel   ALL= NOPASSWD: /usr/bin/chvt

Almost EOF …

More or less obviously this is very hacky, has multiple issues and falls apart very easily, somewhere in the next few months I'll try to find a cleaner way to implement this without relying on too much on shell scripting and firing signals at processes using pidfiles.

Until that happens I'll continue to use my sxmo screenlocker on my phone because it mostly works well enough for what I need.