Universal environment variables with pam_env
One of the minor annoyances of working in Unix-land is the variety of ways to
set user environment variables: .zshenv
, .bash_profile
, .profile
,
.xinitrc
, and
so forth. Depending on where you set them, your variables may be visible
in interactive shells, login shells, X applications, or some strange mixture.
Even worse is that some, but not all, environment variables are set when
interpreting your crontab
, so that has yet another set of rules you have to
remember.
I’ve been gradually working on setting up my home directory so that it
contains everything (barring system packages) that I need to work, so that
migrating to a new computer is just a matter of installing packages and then
copying my $HOME
. This only works if I have a consistent way to set
environment variables, so that they are visible to every program that
runs under my account, regardless of how I logon. And I think I’ve finally
got it.
This process requires changing a couple of system-wide settings, but only in special circumstances:
If you care about having environment variables enabled when you login via TTY or SSH, make the following change to
/etc/pam.d/login
: find the linesession required pam_env.so readenv=1 envfile=/etc/default/locale
and change it to read
session required pam_env.so readenv=1 user_readenv=1 envfile=/etc/default/locale
(In the default config,
pam_env
is loaded only when you login via your display manager: MDM on Mint, GDM on Gnome, etc.)If you want to set
LD_LIBRARY_PATH
then you have to make a change to the Xsession settings: add a file90preserve_ld_library_path
to/etc/X11/Xsession.d/
containingSTARTUP="/usr/bin/env LD_LIBRARY_PATH=${LD_LIBRARY_PATH} ${STARTUP}"
This is only necessary because X normally starts ssh-agent which clears `LDLIBRARYPATH for security reasons. Another option is to disable loading of ssh-agent entirely.
If you want your environment variables to apply to your crontab (and you do), edit
/etc/pam.d/cron
and find the linesession required pam_env.so envfile=/etc/default/locale
with
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
After making that change(s), create a file ~/.pam_environment
and add your
variables to it like this:
# HOME is not defined when this file is executed, and BOOST_ROOT needs
# to be set somewhere, so...
HOME DEFAULT=/home/@{PAM_USER}
BOOST_ROOT DEFAULT=${HOME}/bin/boost
# Setup path
PATH DEFAULT=${HOME}/bin:${HOME}/bin/bin:${PATH}
PATH DEFAULT=${HOME}/bin/rakudo:${PATH}
PATH DEFAULT=/opt/texlive/2016/bin/x86_64-linux:${PATH}
PATH DEFAULT=/opt/teyjus:${PATH}
PATH DEFAULT=/opt/bin:${PATH}
PATH DEFAULT=${HOME}/.cabal/bin:${PATH}
PATH DEFAULT=${HOME}/.local/bin:${PATH}
PATH DEFAULT=/opt/cabal/1.20/bin:/opt/ghc/7.8.4/bin:${PATH}
PATH DEFAULT=${HOME}/bin/tmsu-x86_64-0.6.1/bin:${PATH}
PATH DEFAULT=${HOME}/bin/boost:${PATH}
# Python paths
PYTHONPATH DEFAULT=${HOME}/.local/lib/python/site-packages:${PYTHONPATH}
PYTHONPATH DEFAULT=${HOME}/.local/lib/python2.7/site-packages:${PYTHONPATH}
# Manpaths
MANPATH DEFAULT=/opt/texlive/2014/texmf/doc/man:${MANPATH}
MANPATH DEFAULT=${HOME}/docs/man:${MANPATH}
MANPATH DEFAULT=${HOME}/bin/man:${MANPATH}
# Infopaths
INFOPATH DEFAULT=/opt/texlive/2016/texmf/doc/info:${INFOPATH}
# Library/link paths
LD_LIBRARY_PATH DEFAULT=/usr/local/lib:${BOOST_ROOT}/stage/lib:${HOME}/lib:${HOME}/lib/sfml:${LD_LIBRARY_PATH}
LIBRARY_PATH DEFAULT=${HOME}/lib:${HOME}/lib/sfml:${BOOST_ROOT}/stage/lib:${LIBRARY_PATH}
# Include paths
CPLUS_INCLUDE_PATH DEFAULT=${HOME}/include:${BOOST_ROOT}:${CPLUS_INCLUDE_PATH}
# TeX paths
TEXINPUTS DEFAULT=.:${HOME}/.texmf/tex/:
BIBINPUTS DEFAULT=.:${HOME}/.texmf/tex/:
This is my actual .pam_environment
. Setting environment variables that
refer to other, existing variables (e.g., extending $PATH
) uses a fancy
syntax:
NAME DEFAULT=...
Within the value, you can use ${...}
to expand environment variables, and use
@{...}
to expand PAM_ITEM
s (as I did to get the user’s login name, because
$USER
isn’t set yet).
After that, it’s basically a matter of setting things the way you want. Note that if you aren’t using any expansions, you can write just
NAME=value...
With this setup, all of my environment variables are available to any process
that runs under my account, including cron jobs! No more fighting with my
editor to get it to use my $PATH
when it runs my build tools, and no more
hard-coded paths in crontab, etc.