path_helper

Today I learned about path_helper, because I wondered why root's PATH variable contained paths from the user executing sudo:

alice$ sudo -i
root# for e in $(echo ${PATH} | sed 's/:/ /g'); do echo "${e}" | sort; done
/usr/local/bin
/System/Cryptexes/App/usr/bin
/usr/bin
/bin
/usr/sbin
/sbin
/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin
/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin
/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin
/Library/TeX/texbin
/opt/homebrew/bin
/opt/homebrew/sbin
/Users/alice/bin                  <---
/Users/alice/.local/bin           <---
/Users/alice/.local/venv/bin      <---

Where are all these PATH elements coming from? Looking around we find this in /etc/profile:

$ cat /etc/profile     
[...]
if [ -x /usr/libexec/path_helper ]; then
        eval `/usr/libexec/path_helper -s`
fi

There's even a fork a new path_helper implementation that tackles a few of its shortcomings, but let's not give up on it yet. The manpage to path_helper states:

The path_helper utility reads the contents of the files in the directories 
/etc/paths.d and /etc/manpaths.d and appends their contents to the PATH and
MANPATH environment variables respectively.
[...]
Prior to reading these directories, default PATH and MANPATH values are
obtained from the files /etc/paths and /etc/manpaths respectively.

OK, that explains where all these (non-existing) cryptexd paths are coming from:

$ grep -hr . /etc/paths* | sort -u
/bin
/Library/TeX/texbin
/sbin
/System/Cryptexes/App/usr/bin
/usr/bin
/usr/local/bin
/usr/sbin
/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin
/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin
/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin

But there's nothing in there to include the calling user's PATH elements too. So maybe path_helper looks a the wrong user then?

alice$ sudo -i
# whoami
root

# who -m  
alice     ttys006      22 Sep 09:45 

A quick workaround would be use sudo in combination with su, although who -m holds the same information:

alice$ sudo su -
root# who -m
alice     ttys006      22 Sep 09:45 

root# eval $(/usr/libexec/path_helper)                                     
root# for e in $(echo ${PATH} | sed 's/:/ /g'); do echo "${e}" | sort; done
/usr/local/bin
/System/Cryptexes/App/usr/bin
/usr/bin
/bin
/usr/sbin
/sbin
/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin
/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin
/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin
/Library/TeX/texbin

With not much to go by we can still workaround this in our own ~/.profile:

if [ -n "${SUDO_USER}" ]; then
      PATH_NEW=$(for e in $(echo ${PATH} | sed 's/:/ /g'); do
              echo ${e}
      done | grep -ve /Users/ \
              -e /opt/homebrew/ \
              -e /var/run/com.apple.security.cryptexd/ \
              -e /Library/TeX/texbin \
              -e /System/Cryptexes | xargs echo | sed 's/ /:/g')
      export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:${PATH_NEW}"
fi

Yes, it's not pretty, but it does the job. Maybe I should switch to yb66/path_helper after all?