Switching to .pam_environment for environment variables


C: Linux

The past month my system environment was in quite a mess.

I used to put my environment variables in ’(filepath "~/.profile"). However, one day, the file simply stopped being sourced. I tried to switch my display manager from sddm, to gdm, then to lightdm, to no avail. I know sourcing a file on login is somewhat insecure, and ’(filepath "~/.pam_environment") is probably the right way going forward, so I started moving variables into it.

Preprocessing .pam_environment

I had a piece of code in my ’(filepath "~/.profile") that checks Racket and Ruby’s versions and sets the PATH to point to raco and gem‘s bin directory. Now, ’(filepath "~/.pam_environment") can’t do that, so I wrote a Pollen version of the file and rendered / compiled it into the final ’(filepath "~/.pam_environment").

’(div ((class “highlight”)) (table ((class “sourcetable”)) (tbody (tr (td ((class “linenos”)) (div ((class “linenodiv”)) (pre " 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n36"))) (td ((class “code”)) (div ((class “source”)) (pre (span) (span ((class “n”)) "◊") (span ((class “p”)) "(") (span ((class “k”)) “define”) " " (span ((class “p”)) "(") (span ((class “n”)) “defpam_env”) " " (span ((class “n”)) “name”) " " (span ((class “o”)) ".") " " (span ((class “n”)) “contents”) (span ((class “p”)) ")") "\n " (span ((class “p”)) "(") (span ((class “k”)) “define”) " " (span ((class “n”)) “content”) " " (span ((class “p”)) "(") (span ((class “nb”)) “string-join”) " " (span ((class “n”)) “contents”) " " (span ((class “s2”)) """ """) (span ((class “p”)) "))") "\n " (span ((class “n”)) "◊string-append") (span ((class “p”)) "{") (span ((class “n”)) "◊|name|") " " (span ((class “n”)) “DEFAULT=◊|content|”) (span ((class “p”)) "})") "\n\n" (span ((class “n”)) "◊") (span ((class “p”)) "(") (span ((class “k”)) “define”) " " (span ((class “p”)) "(") (span ((class “n”)) “pathlist-string”) " " (span ((class “o”)) ".") " " (span ((class “n”)) “args”) (span ((class “p”)) ")") "\n " (span ((class “p”)) "(") (span ((class “nb”)) “string-join”) " " (span ((class “p”)) "(") (span ((class “nb”)) “string-split”) " " (span ((class “p”)) "(") (span ((class “nb”)) “string-join”) " " (span ((class “n”)) “args”) " " (span ((class “s2”)) """ """) (span ((class “p”)) ")") " " (span ((class “s2”)) """) (span ((class “se”)) "\n") (span ((class “s2”)) """) (span ((class “p”)) ")") " " (span ((class “s2”)) """ ":" """) (span ((class “p”)) "))") "\n\n" (span ((class “n”)) "◊") (span ((class “p”)) "(") (span ((class “k”)) “define”) " " (span ((class “n”)) “racket-version”) " " (span ((class “p”)) "(") (span ((class “nb”)) “version”) (span ((class “p”)) "))") "\n" (span ((class “n”)) "◊") (span ((class “p”)) "(") (span ((class “k”)) “define”) " " (span ((class “n”)) “ruby-version”) "\n " (span ((class “c1”)) "; this, or sed | cut? I don" 39 “t know which I prefer really.”) "\n " (span ((class “p”)) "((") (span ((class “nb”)) “compose1”) " " (span ((class “nb”)) “first”) "\n " (span ((class “p”)) "(") (span ((class “k”)) “λ”) " " (span ((class “p”)) "(") (span ((class “n”)) "$1") (span ((class “p”)) ")") " " (span ((class “p”)) "(") (span ((class “nb”)) “string-split”) " " (span ((class “n”)) "$1") " " (span ((class “s2”)) """ “p” """) (span ((class “p”)) "))") "\n " (span ((class “nb”)) “second”) "\n " (span ((class “p”)) "(") (span ((class “k”)) “λ”) " " (span ((class “p”)) "(") (span ((class “n”)) "$1") (span ((class “p”)) ")") " " (span ((class “p”)) "(") (span ((class “nb”)) “string-split”) " " (span ((class “n”)) "$1") " " (span ((class “s2”)) """ " " """) (span ((class “p”)) ")))") "\n " (span ((class “p”)) "(") (span ((class “nb”)) “with-output-to-string”) " " (span ((class “p”)) "(") (span ((class “k”)) “λ”) " " (span ((class “p”)) "()") " " (span ((class “p”)) "(") (span ((class “nb”)) “system”) " " (span ((class “s2”)) """ “ruby —version” """) (span ((class “p”)) ")))))") "\n\n" (span ((class “n”)) "◊") (span ((class “c1”)) "; == directory shortcuts ==") "\n" (span ((class “n”)) "◊defpam_env") (span ((class “p”)) "[") (span ((class “s2”)) """ “XDG_DESKTOP_DIR” """) (span ((class “p”)) "]{") (span ((class “n”)) "◊|HOME|/デスクトップ") (span ((class “p”)) "}") "\n" (span ((class “n”)) "◊defpam_env") (span ((class “p”)) "[") (span ((class “s2”)) """ “XDG_DOWNLOAD_DIR” """) (span ((class “p”)) "]{") (span ((class “n”)) "◊|HOME|/ダウンロード") (span ((class “p”)) "}") "\n" (span ((class “n”)) "◊") (span ((class “c1”)) "; …") "\n\n" (span ((class “n”)) "◊defpam_env") (span ((class “p”)) "[") (span ((class “s2”)) """ “PATH” """) (span ((class “p”)) "]{") (span ((class “n”)) "◊pathlist-string") (span ((class “p”)) "{") "\n" (span ((class “n”)) "◊|HOME|/git/scripts") "\n" (span ((class “n”)) "◊|HOME|/git/Sudocabulary") "\n" (span ((class “n”)) "◊|HOME|/bin") "\n" (span ((class “n”)) "◊|HOME|/.racket/◊|racket-version|/bin") "\n" (span ((class “n”)) "◊|HOME|/.local/share/npm-global/bin") "\n" (span ((class “n”)) "◊|HOME|/.gem/ruby/◊|ruby-version|/bin") "\n" (span ((class “n”)) "$◊") (span ((class “s2”)) """ "{" """) (span ((class “n”)) “PATH◊”) (span ((class “s2”)) """ "}" """) "\n" (span ((class “n”)) "/usr/bin") " " (span ((class “n”)) "◊") (span ((class “c1”)) "; safety fallback") "\n" (span ((class “p”)) "}}") "\n\n" (span ((class “n”)) "◊") (span ((class “c1”)) "; …") "\n\n" (span ((class “n”)) "◊defpam_env") (span ((class “p”)) "[") (span ((class “s2”)) """ “VISUAL” """) (span ((class “p”)) "]{") (span ((class “n”)) “nvim”) (span ((class “p”)) "}") "\n" (span ((class “n”)) "◊defpam_env") (span ((class “p”)) "[") (span ((class “s2”)) """ “EDITOR” """) (span ((class “p”)) "]{") (span ((class “n”)) “nvim”) (span ((class “p”)) "}") "\n")) "\n")))) "\n")

It’s really ugly, but it only has one job, which it does fine.

DEFAULT? OVERRIDE? pam_env, what?

All seemed well, until a few days later my PATH config isn’t taking effect anymore. For some reason, every other variable is properly set, just not PATH. Trying to see just exactly which file those variables came from, I noticed a variable that is only present in my ’(filepath "~/.pam_environment"), telling me that it is indeed being read. I looked up info on PATH not being set with ’(filepath ".pam_environment"), and found this StackExchange post hinting at something to do with the var DEFAULT=value syntax.

Reading into pam_env’s documentation on the config format, it seems that DEFAULT is intended to be used for, well, defaults that could be overridden. Changing it into OVERRIDE seems to tell pam_env to just use the value and don’t fiddle around.

’(div ((class “highlight”)) (table ((class “sourcetable”)) (tbody (tr (td ((class “linenos”)) (div ((class “linenodiv”)) (pre “1\n2\n3\n4”))) (td ((class “code”)) (div ((class “source”)) (pre (span) (span ((class “n”)) "◊") (span ((class “p”)) "(") (span ((class “k”)) “define”) " " (span ((class “p”)) "(") (span ((class “n”)) “defpam_env”) " " (span ((class “n”)) “name”) " " (span ((class “o”)) ".") " " (span ((class “n”)) “contents”) (span ((class “p”)) ")") "\n " (span ((class “p”)) "(") (span ((class “k”)) “define”) " " (span ((class “n”)) “content”) " " (span ((class “p”)) "(") (span ((class “nb”)) “string-join”) " " (span ((class “n”)) “contents”) " " (span ((class “s2”)) """ """) (span ((class “p”)) "))") "\n " (span ((class “c1”)) "; DEFAULT -" ">" " OVERRIDE.") "\n " (span ((class “n”)) "◊string-append") (span ((class “p”)) "{") (span ((class “n”)) "◊|name|") " " (span ((class “n”)) “OVERRIDE=◊|content|”) (span ((class “p”)) "})") "\n")) "\n")))) "\n")

The full configuration file can be found in my dotfiles repo.