Recently, I have uninstalled my display manager for various reasons. One of the reasons for that was to have a better understanding of my login process. Display managers generally do arbitrary stuff, they have their own way/order of sourcing files, sometimes they skip important files and sometimes they source unnecessary stuff etc. So I thought, using no display manager would result in a simpler login model. Oh boy, I was wrong.
After quite a bit of fiddling around I believe I have a clear mental model of in which order my files gets sourced. Before that, here is a quick summary of some terminology:
When you log into a TTY, you get a interactive login shell. When you spawn a new terminal in your DE/WM, you get a non-login interactive shell. When you log in to your DE directly, that is done through a non-interactive login shell. A script that you run is run under a non-login non-interactive shell. These are important because this information will guide you why your PATH variable is not working as intended in some cases and why sometimes it works.
I use zsh as my main shell and I also set it to be my login shell. zsh has a much simpler sourcing hierarchy, I'm not even going to bother myself explaining how bash does it. So here is how it goes:
/etc/zshenv and then ~/.zshenv is sourced. This is done no matter what type of shell you are spawning. These files gets sourced every time you are spawning a shell.
/etc/zshenv
/etc/zsh/zshenv instead of /etc/zsh even if man zsh says the exact opposite. Some distributions change this path for zsh and they forgot to update the manual.~/.zshenv
$PATH, $PAGER etc.)./etc/zprofile gets sourced at this point, and then ~/.zprofile. Just to make it clear, these files are only sourced at login and never again (if you somehow spawn a login shell again, of course it will be sourced again).
/etc/zprofile
/etc/zsh/zprofile instead of /etc/zprofile.
Most of the distributions have the following line inside the /etc/zprofile file:
emulate sh -c 'source /etc/profile'
/etc/profile file (which automatically gets sourced if you are using sh or bash as your login shell.), which in turn sources files under /etc/profile.d. These files are generally gets installed when you install a program into your system. For example, if you install nix package manager, it also installs /etc/profile.d/nix{,-daemon}.sh files and this files needs to be sourced for nix to work properly. So if your /etc/zprofile or /etc/zsh/zprofile does not contain this line, add it to your ~/.zprofile file.~/.zprofile
Also this is the file that you want to run startx, if you are not using a display manager like me. Here is how I do it:
# Following automatically calls "startx" when you login on tty1:
if [[ -z ${DISPLAY} && ${XDG_VTNR} -eq 1 ]]; then
# Logs can be found in ~/.xorg.log
exec startx -- -keeptty -nolisten tcp > ~/.xorg.log 2>&1
fi
After running startx, it loads ~/.xinitrc file. This is the file that you want to start your window manager and all the other programs that you want to see at startup. An example file might be:
#!/bin/sh
# The following is essential, you need to source
# every file under `/etc/X11/xinit/xinitrc.d`.
if [[ -d /etc/X11/xinit/xinitrc.d ]] ; then
for f in /etc/X11/xinit/xinitrc.d/?*.sh ; do
echo "Sourcing $f"
[[ -x "$f" ]] && . "$f"
done
unset f
fi
sysresources=/etc/X11/xinit/.Xresources
sysmodmap=/etc/X11/xinit/.Xmodmap
[[ -f $sysresources ]] && xrdb -merge $sysresources
[[ -f $sysmodmap ]] && xmodmap $sysmodmap
# Load your keymap and all that jazz
setxkbmap 'us(intl)'
xrdb -merge $HOME/.Xresources
xmodmap $HOME/.Xmodmap
# Cursor
xsetroot -cursor_name left_ptr
# Starting programs like your compositor makes sense here
picom -b
# Some other programs related to X
unclutter &
redshift &
# Run your WM, as an example I run bspwm
exec bspwm
/etc/zshrc (or /etc/zsh/zshrc) and then ~/.zshrc gets sourced, if you are spawning an interactive shell.
/etc/zlogin (or /etc/zsh/zlogin) and then ~/.zlogin gets sourced.
~/.zprofile or move all of your ~/.zprofile into this one.~/.zshrc (if it's sourced at all) and it doesn't make much sense (to me).
A problem I was having was related to unison. It's a simple and powerful file synchronization program that I use. One problem with that is, it's quite picky about it's version. It requires absolute same version on both client and server, and not only that, it also wants both binaries to be compiled with same version of the OCaml compiler. To solve this kind of version issue between my rigs, (among other reasons) I use nix package manager. This gets me same binary on every computer I have. But there is one problem, when I install unison through nix, it installs it under ~/.nix-profile/bin/unison, and when I run unison to synchronize my files, it fails to find unison on the other computer. But I can ssh into other computer and run unison without a problem. Hmm, what is going on here?
nix gets installed it also installs the following files: /etc/profile.d/nix{,-daemon}.sh$PATH variable, so ~/.nix-profile/bin gets added into $PATH./etc/profile, which is sourced by /etc/zsh/zprofile./etc/zsh/zprofile gets sourced when a login shell is spawned.
So when I do ssh my-machine, it spawns an interactive login shell and drops me into it. By this time, /etc/zsh/zprofile is already sourced and thanks to that I can simply run unison binary. When I run unison on client, it connects the server through ssh but it uses a non-login non-interactive shell while doing that. Same goes for this command ssh my-machine which unison. This command runs in a non-login non-interactive shell. Because /etc/zsh/zprofile requires a login shell to get sourced, ~/.nix-profile/bin never gets added to the $PATH variable.
So what's the solution? You know it, only ~/.zshenv gets sourced when non-login non-interactive shell is spawned. So I just update the $PATH there and everything works as expected now.
That's it. This was just some kind of a braindump. From now on, I'll just try to drop my notes as blog posts like this.
Comments