PATH and other evironment issues in Leopard

PATH and path_helper

Leopard introduced this great(?) new way of setting up default PATH
and and MANPATH, in /usr/libexec/path_helper. Some people don't
like it much, but I think it's not bad, and it's perfectly workable
once you understand all of the issues.

The idea is simple. /etc/paths contains the initial PATH values, one
path per line, in order. /etc/paths.d contains additional files, with
paths to be added to your PATH. They'll be added based on the sort
order of the filenames there. This does make me wish they'd called
the one default file in there 50_X11, instead of X11, so that they'd
created a precedent of numbering things in desired order. But they
didn't. You can change it, or you can add other prefixes to anything
you add there. It's also worth mentioning that if a path is repeated
in more than one place, it is NOT added more than once. The first location
wins.

But how does it really work?

First of all, path_helper seem to be run exclusively out of /etc/profile
and /etc/csh.login. This means that for things that are not login shells,
path_helper is NOT run (for both types of shell). An example would be
starting an xterm with +ls, or running a command in ssh (e.g.
'ssh hostname who'). However /etc/bashrc,
or /etc/csh.cshrc are still both run for interactive shells, and so ar
the user's .bashrc or .cshrc files. So if the user customizes their PATH
in these files, these changes will still show up.

Of course a shell which is not interactive (e.g. a script) shouldn't
read ANY of these
files, and in that case it will simply get the initial default PATH.
But sh and csh behave differently. If there is no PATH at all, sh/bash
uses a compiled-in PATH of "/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:."
(as of Leopard 10.5.5).
csh/tcsh does not use a compiled-in PATH, but it DOES source
/etc/csh.cshrc, and $HOME/.cshrc. Yes that's right, these are sourced
even when running shell scripts (c.f. "use of csh considered harmful").

Second of all, /etc/paths does not actually contain initial PATH values.
You can prove this by removing this file, and noting that you don't
get rid of these paths (except for /usr/local/bin). /etc/paths does
contain paths that are added first, but they are ADDED to the current
PATH, they don't replace it. The initial PATH of
"/usr/bin:/bin:/usr/sbin:/sbin" seems to be hardcoded in /sbin/launchd.
This means that anything you want to
set as a true default initial starting PATH, is best set in /etc/profile
and /etc/csh.login, before the call to path_helper. In my case, I wanted
to take out the sbin directories (these directories should contain commands
for admins, not for general users).

Third, X11 is special. If you just run "xterm" from a Terminal window,
Leopard very cooly automatically starts up X11, but it won't be that
surprising that the xterm that runs inherits the environment from the
Terminal window. But there's still some funky stuff going on.
The script /usr/X11/bin/startx adds two things to your path, if they
aren't there already; /usr/X11/bin gets added to the beginning of your
path, and /usr/X11R6/bin gets added to the end of your path. This is
kind of stupid since under Leopard these are both the same directory.
But the other weird thing is that startx is started by
/System/Library/LaunchAgents/org.x.startx.plist (which isn't the weird
part). If you peek in there, you'll notice that it explicitly calls
this script by calling bash with the --login option. What this means is
that X11 gets started using /etc/profile, and hence path_helper.
And what THIS means is that an xterm started with command-N (⌘-N)
may have a different environment (inherited from X11) than if you start xterm
from the command line in a Terminal window.

(Feb '09) One more thing I've found about X11. When you start X11 by
clicking on the X11 icon, it (by default) starts an xterm too. But the
xterm started here is not a child of X11, it's started in parallel, and
it is started without using the path_helper stuff. So it won't have the
same system-supplied environment that any subsequent
applications started by X11 will have.

To fully control the user's PATH, I'd suggest setting it to empty
in /etc/profile and /etc/csh.login immediately before path_helper
is called, and then use the mechanism they provide to give an initial
path in /etc/paths, and then provide path packages in /etc/paths.d
BUT in light of the weird xterm problem described above, perhaps
scrapping the whole thing and doing traditional user dot file setup is the
way to go.

Cron jobs are a whole 'nother thing too, and maybe I'll get into that
at some point.

MANPATH

MANPATH can be controlled in the same way that PATH can. The parallel
files /etc/manpaths and /etc/manpaths.d/* exist, and work just the same.
I don't think launchd provides any default values, so whatever is in
/etc/manpaths really does work out to be the default.

However, man can also be configured using /etc/man.conf. But much of
this file is ignored if MANPATH is set. If you prefer /etc/man.conf,
then my suggestion would be to either clear out /etc/manpaths and
/etc/manpaths.d/*, or just remove the variable MANPATH after path_helper
is called in /etc/profile and /etc/csh.login ("unset MANPATH"
for sh/bash, and "unsetenv MANPATH" for csh/tcsh).

X11 environment

You may notice that you can start any X11 command, and that causes
X11 itself to start -- one of the cute uses of launchd. But for this to work,
the DISPLAY environment variable must be set even though X11 isn't running.
This is set by launchd, but not the process ID 1 system launchd that functions
like init on other Unix systems. When you log in, the system starts a
user-level launchd, and this is what sets your DISPLAY variable, and handles
the as-needed launching of X11. This has some important ramifications
in configuring X11.

So far the only X11 config I've had to do was for MacPorts and Fink.
If you add either or both of these, you'll find that those X11-based
applications may not find the app-defaults files that they came with.
The problem is that the default search path, which seems to be compiled
in to libXt, doesn't look in the new locations. Environment variables
XFILESEARCHPATH and XUSERFILESEARCHPATH can be used to tell libXt where
to look.

I haven't found an excellent solution here. I add to the user's
startup files that if they're on a Darwin system, they get the variable
XUSERFILESEARCHPATH set to
"/opt/local/lib/X11/app-defaults/%N:/sw/etc/app-defaults/%N". Note that
one really strange gotcha here is that other percent-things (like %T)
don't work here,
so a path that WOULD work in XFILESEARCHPATH will NOT work in
XUSERFILESEARCHPATH.
Apparently this wierdness is true on all platforms, not just OS X.

You can't really add this to /usr/X11/bin/startx. In a traditional
environment this would be ok, because most X11 clients are descendenants
of the X server. Because of the
launchd tricks, this isn't true on Leopard. You can start X things from
anywhere that's a child
of the user-level launchd. In practice this will probably just be shells,
so you really want your default dot files to set up this environment.
It would be great if there was some file where you could configure variables
set by launchd, or (specific to this case), if there was a file where
you could configure the libXt search paths.

In a more perfect world

It'd be nice if I could put group-readable files into /etc/paths.d,
and only members of that group would get that path. This does in fact
work if you are willing to live with "Permission denied" error messages
for all users not in those groups.

Even cooler would be a subscription-based thing. Default system files
would have things like "subscribe core,x11,fink", and this would take care of
all variables for those packages in one swoop. Then the user could
do things like "unsubscribe fink; subscribe admin,macports" to alter their
entire environment from the default.
By the way, I'm pretty sure this idea was suggested to me about 20 years ago
by J Greely, and who knows where he got it.

Reader Comments
(Experimental. Moderated, expect delays. Posts may be edited or ignored.
I reserve the right to remove any or all comments, at any time.)

6 comments:

At 2009/02/22 18:48JosephN wrote:

Just wanted to drop a quick note here about

~/.MacOSX/environment.plist

This file defines the environment variables for normal applications (i.e. not anything executed in a shell or terminal window). You will likely have to create this file and directory yourself. First touch the file, then open it in the property list editor and add key value pairs.

The reason this is interesting is if you put PATH and MANPATH in this plist file then any shell you start will include these environment variables as read in from this file (unless you overwrite them locally by declaring a new PATH variable).

So really the path helper script and organization is a total waste. The PATH and MANPATH variables should be defined in the environment.plist file so your normal apps have access to the same environment as your shell apps do.

At 2009/02/22 19:17 wrote:

Thanks. I'll look into this when I get a chance. I'm worried it might become deprecated.

At 2011/01/21 11:31SteveL wrote:

If you set the PATH environment variable in ~/.MacOSX/environment.plist some applications, like X11, will fail to launch.

For more information check out the post by David Whetstone at http://discussions.apple.com/thread.jspa?threadID=2149229&tstart=0

At 2011/03/14 12:11 wrote:

FYI, just one typo: /etrc/profile

Thanks for writing this post, it is helping me learn more about PATH.

I'm trying to find out if entering a comment: #whatever into /etc/paths is OK, or if it breaks that (or everything afterwards). When I do $ echo $PATH it shows my comment line.

I don't know at what point this behaviour changed, but as of right now (OS X 10.6.8), path_helper(8) no longer prints the commands to set $MANPATH, unless $MANPATH is is already defined (even if zero-length).

This means that path_helper's behaviour now differs from that described in its own man page.

I would guess that this change was made to enable the use of man.conf(8) by default. However, it does mean that any changes made to /etc/manpaths or /etc/manpaths.d/* will not take effect unless one manually invokes path_helper from shell startup scripts.

If I may update my comment of yesterday: It appears that path_helper(8)'s man page does in fact describe its behaviour correctly. It contains the following parenthesised sentence: "(The MANPATH environment variable will not be modified unless it is already set in the environment.)"

It's also very clear from the path_helper.c source file that it only adjusts MANPATH if it's already set: