# are legitimate reasons to want to do this, and in any case the shell
# should not choke or cause unexpected problems should it happen anyway.
-### detect cygwin
-[[ $OSTYPE =~ (cygwin|msys)* ]] && is_cygwin=
-
-### exports
-## lang
+### lang
export CHARSET=UTF-8
export LANG=en_US.UTF-8
export LC_CTYPE=$LANG
-
-## msys2
-if [[ -v MSYSTEM && ! -v _sev_setup_msys2 ]] {
- # path mangling exclusions for gpg-connect-agent
- # https://www.gnupg.org/documentation/manuals/gnupg/Controlling-gpg_002dconnect_002dagent.html
- export MSYS2_ARG_CONV_EXCL="${MSYS2_ARG_CONV_EXCL:+$MSYS2_ARG_CONV_EXCL;}\
-/echo;/let ;/definq;/datafile ;/showdef;/cleardev;/sendfd ;/recvfd;/open ;\
-/close ;/showopen;/serverpid;/sleep;/hex;/nohex;/decode;/nodecode;/subst;\
-/nosubst;/while ;/if ;/end;/run ;/bye;/help"
- # ssh called from mingw64-git attempts to convert path to Windows, and
- # causes it to choke. paths are converted to *nix before exporting and
- # will work if cygwin ssh is installed (default).
- export MSYS2_ENV_CONV_EXCL=_GNUPG_SOCK_
- export _sev_setup_msys2=
-}
-
-
-## path
+### path
# NOTE: we utilize the fact that unique arrays keep the first occurrence and
# remove any further occurences to capture elements from the old PATH
# that we did not anticipate and shift them to the front, since they are
# probably important to the system
if [[ ! -v _sev_setup_path || -o login ]] {
- typeset -a winpath
- if [[ -v is_cygwin ]] {
- windir=$(cygpath -uW)
- sysdir=$(cygpath -uS)
- winpath=($sysdir $windir $sysdir/Wbem
- $sysdir/WindowsPowerShell/v1.0
- $sysdir/../SysWOW64 $sysdir/../SysWOW64/Wbem
- $sysdir/../SysWOW64/WindowsPowerShell/v1.0)
- for (( i = 1; i <= $#winpath; i++ )) {
- winpath[$i]=${winpath[$i]:a}
- }
- unset windir sysdir
- }
typeset -U path fpath
# add as many generic paths as possible to keep the order we want
# NOTE: /usr/{local,pkg,games} are unix/bsdisms
syspath=("$path[@]")
path=({~/,/,/usr/}sbin /opt/{s,}bin /usr/local/{s,}bin /usr/pkg/{s,}bin
/usr/X11R{7,6}/bin /usr/games {~/,/,/usr/}bin)
- ((ulen=$#path))
- [[ -v is_cygwin ]] && path=("$path[@]" "$winpath[@]")
- ((wlen=$#path))
+ ((len=$#path))
path=("$path[@]" "$syspath[@]")
# remove nonexistent and duplicate paths
for (( i = 1; i <= $#path; i++ )) {
if [[ ! -e $path[$i] ]] {
path[$i]=()
- ((i <= ulen)) && ((ulen--))
- ((i <= wlen)) && ((wlen--))
+ ((i <= len)) && ((len--))
((i--))
continue
}
- if [[ ! -v is_cygwin ]] || (( i <= ulen )) { continue }
- # Windows only: remove cygwin-ified duplicates case-insensitively
- c=$(cygpath -u -- ${(L)path[$i]})
- for (( j = i + 1; j <= $#path; j++ )) {
- if [[ $c == $(cygpath -u -- ${(L)path[$j]}) ]] {
- path[$j]=()
- # NOTE: likelihood of our defined windows path being duplicate
- # is low, but just in case
- ((j <= wlen)) && ((wlen--))
- ((j--))
- }
- }
- unset c
}
- (( wlen > 0 )) && path=("${(@)path[wlen + 1, -1]}" "${(@)path[1, wlen]}")
- unset winpath syspath ulen wlen i j
+ # shift valid system paths to the front if there are any left
+ ((len > 0 && len < $#path)) && path=("${(@)path[len + 1, -1]}" "${(@)path[1, len]}")
+ unset syspath len i j
# include our zsh dir in fpath. unlike above, we always prefer our paths
fpath=(${ZDOTDIR:-~/.zsh}/functions/{*,Completions/*}(N) "$fpath[@]")
# FPATH is not exported by default
export _sev_setup_path=
}
-## xdg
+### temp
+# NOTE: it's intentional to separate POSIX tmp for each session (spec says
+# programs should not expect data there to be long-lived) and to keep the
+# same runtime dir and not create a new one if a new login shell is
+# spawned, since the XDG spec calls for the same dir to be utilized for
+# each "session".
+if [[ ! -v _sev_setup_tmp ]] {
+ t=${TMPDIR:-${TEMP:-${TMP:-/tmp}}}/.home-$LOGNAME
+ h=~/tmp
+ [[ ! -e $t ]] && mkdir -pm700 $t 2>/dev/null
+ if [[ ! -d $t ]] {
+ [[ -o interactive ]] &&
+ print -P "%F{red}!!! Can't create temp dir $t%f"
+ # fallback bare directories
+ [[ -h $h ]] && unlink $h 2>/dev/null
+ [[ ! -e $h ]] && mkdir -m700 $h 2>/dev/null
+ }
+ # [re-]create link to our tmp
+ [[ -h $h || ! -e $h ]] && ln -sfn $t $h 2>/dev/null
+ # finally create our subdir for this session
+ export _sev_tmp=$h/.session.$$
+ # ensure dir doesn't exist. if there is already something there it is
+ # likely a stale directory or something is very broken—assume the former.
+ # the user could also want dirs recreated by unsetting the var.
+ if [[ -h $_sev_tmp ]] {
+ unlink $_sev_tmp 2>/dev/null
+ } elif [[ -e $_sev_tmp ]] {
+ rm -rf $_sev_tmp 2>/dev/null
+ }
+ mkdir -m700 $_sev_tmp 2>/dev/null
+ export TMPDIR=$_sev_tmp TEMP=$_sev_tmp TMP=$_sev_tmp
+ unset t h
+ export _sev_setup_tmp=
+}
+
+### xdg
if [[ ! -v _sev_setup_xdg ]] {
# merge with any existing dirs and remove duplicates using unique arrays
- # NOTE: include and then remove .config and .local/share to ensure it is
- # not present in the array if it was added before we got to it
+ # NOTE: include and then remove CONFIG_HOME and DATA_HOME to ensure they
+ # are not present in the array if it was added before we got to it
typeset -UT XDG_CONFIG_DIRS xdg_config_dirs
typeset -UT XDG_DATA_DIRS xdg_data_dirs
export XDG_CONFIG_HOME=$HOME/etc
"${XDG_DATA_DIRS:+${xdg_data_dirs[@]}}")
export XDG_DATA_DIRS=${XDG_DATA_DIRS#$XDG_DATA_HOME}
# use our custom tmp for cache and runtime
- export XDG_CACHE_HOME=~/tmp
- # NOTE: it's intentional to keep the same runtime dir for the whole session
- # and not create a new one if a new login shell is spawned, since the
- # spec calls for the same dir to be utilized for each "session".
- export XDG_RUNTIME_DIR=~/tmp/xdg.$$
-}
-
-## temp
-if [[ ! -v _sev_setup_tmp ]] {
- t=${TMPDIR:-${TEMP:-${TMP:-/tmp}}}/.home-$LOGNAME
- if [[ ! -e $t ]] {
- mkdir -m700 $t 2>/dev/null
- if [[ ! -d $t ]] {
- [[ -o interactive ]] &&
- print -P "%F{red}!!! Can't create temp folder $t%f"
- # fallback bare directories
- [[ -h $XDG_CACHE_HOME ]] && unlink $XDG_CACHE_HOME 2>/dev/null
- [[ ! -e $XDG_CACHE_HOME ]] && mkdir -m700 $XDG_CACHE_HOME 2>/dev/null
- }
- }
- if [[ -e $t ]] {
- export TMPDIR=$t TEMP=$t TMP=$t
- # [re-]create link to our tmp if safe
- [[ -h $XDG_CACHE_HOME || ! -e $XDG_CACHE_HOME ]] &&
- ln -sf $t $XDG_CACHE_HOME 2>/dev/null
- } else {
- # ensure proper tmp vars, e.g. msys2 does not set TMPDIR
- : ${TMPDIR:=${TEMP:-${TMP:-/tmp}}}
- : ${TEMP:=$TMPDIR}
- : ${TMP:=$TMPDIR}
- }
- unset t
- export _sev_setup_tmp=
-}
-
-## xdg
-if [[ ! -v _sev_setup_xdg ]] {
- # create xdg runtime dir
- # NOTE: spec says the dir should only exist for the lifetime of the
- # session, so if there is already something there it is likely stale
- # or something is very broken—assume the former.
- [[ -e $XDG_RUNTIME_DIR ]] && rm -rf $XDG_RUNTIME_DIR 2>/dev/null &&
- mkdir -m700 $XDG_RUNTIME_DIR 2>/dev/null
+ export XDG_CACHE_HOME=$_sev_tmp/.xdg.cache
+ export XDG_RUNTIME_DIR=$_sev_tmp/.xdg.runtime
+ # create xdg tmp dirs
+ for x in $XDG_CACHE_HOME $XDG_RUNTIME_DIR; do
+ # same as in temp creation, ensure it doesn't exist
+ if [[ -h $x ]]; then
+ unlink $x 2>/dev/null
+ elif [[ -e $x ]]; then
+ rm -rf $x 2>/dev/null
+ fi
+ mkdir -m700 $x 2>/dev/null
+ done
# source user dirs after other vars
[[ -e $XDG_CONFIG_HOME/user-dirs.dirs ]] &&
emulate sh -c "source $XDG_CONFIG_HOME/user-dirs.dirs"
export _sev_setup_xdg=
}
-## gpg forwarding
+### gpg + ssh + forwarding
# NOTE: while ssh manages its auth sock in its protocol when ForwardSsh is
# enabled, GPG must be forwarded manually over Unix socket. to support
# this, we forward the restricted gpg-agent extra socket to the remote
# socket, rather than replace the sockets in GNUPGHOME which will be
# overridden on the next connection. previously, you could provide a path
# to the agent socket in GPG_AGENT_INFO, but that was deprecated in GPG
-# v2.1. instead, we must clone GNUPGHOME and replace the agent sockets
-# there with the forwarded one.
+# v2.1. instead, we must clone GNUPGHOME with links and replace the agent
+# sockets there with the forwarded one.
# NOTE: since Unix sockets are not supported under Windows, this will not work
-# under msys, cygwin, mingw, etc.
+# under msys, cygwin, mingw, etc., but may work under wsl2.
# HACK: without SendEnv, which is disabled by default in most sshd configs,
# there is no foolproof way to prevent race conditions via filename
# collisions or to pass the desired forward path to the remote host
# the host if the path the client wants to use is writable. however, this
# would open up too many edge cases where it wouldn't work or be clunky
# (e.g. asking for password twice) to make it worth it.
-if [[ ! -v _sev_setup_gpg ]] {
- # helper function for decoding gpgconf socket paths
+if [[ ! -v _sev_setup_agents ]] {
function _socketpath {
- # dirs are percent-encoded
- # https://stackoverflow.com/a/64312099
- local x=${1//(#b)%([[:xdigit:]](#c2))/${(#):-0x$match[1]}}
- # remove \r from Windows paths
- if [[ -v commands[cygpath] ]] {
- x=$(cygpath -u -- ${x/%$'\r'} 2>/dev/null)
- }
- echo $x
+ # dirs are percent-encoded: https://stackoverflow.com/a/64312099
+ echo ${1//(#b)%([[:xdigit:]](#c2))/${(#):-0x$match[1]}}
}
+ ## gpg forwarding
if [[ ! -v _sev_gpg_forwarded && -v commands[gpg] ]] {
export _GNUPG_SOCK_DEST_BASE=/tmp/.gpg-agent-forward
export _GNUPG_SOCK_DEST_EXT=$(date +%s).$RANDOM
export _GNUPG_SOCK_DEST=$_GNUPG_SOCK_DEST_BASE.$_GNUPG_SOCK_DEST_EXT
export _sev_gpg_forward_dir=${GNUPGHOME:-~/.gnupg}/.ssh_forward
- # clean up forwards if its session is dead or we ask for it
+ # clean up forward dirs if its session is dead or we ask for it
if [[ -d $_sev_gpg_forward_dir ]] {
find $_sev_gpg_forward_dir -type d -mindepth 1 -maxdepth 1 |
while read -r x; do
# NOTE: the only way we can get here is if we have not been
- # forwarded before. if our own pid already has a dir, it
- # is most likely stale, or something is very broken—
- # assume the former.
+ # forwarded before or if the user asks for it. if our own
+ # pid already has a dir, it is most likely stale, or
+ # something is very broken—assume the former.
p=$(basename $x)
if [[ -v _sev_gpg_forward_clean || $$ == $p ]] ||
! kill -0 $p 2>/dev/null; then
for x in $(gpgconf --list-dirs | grep 'agent-.*-\?socket:'); do
x=$(_socketpath ${x/#agent-*socket:})
if [[ ! -v orig ]] {
+ # move forwarded socket to first valid agent socket path
+ # XXX: if tmp is on different filesystem this may not work
mv $s $x
orig=$x
} else {
+ # make links to forwarded socket for any others
ln -s $orig $x
}
done
}
## cleanup
- # unset gpg helper
unfunction _socketpath
- ## perl local lib
- [[ -v commands[perl] && -d $XDG_DATA_HOME/perl5/lib/perl5 ]] &&
- eval $(perl -I$XDG_DATA_HOME/perl5/lib/perl5
- -Mlocal::lib=$XDG_DATA_HOME/perl5 2>/dev/null)
+ export _sev_setup_agents=
}
-unset is_cygwin
+## perl local lib
+# TODO: debounce this
+[[ -v commands[perl] && -d $XDG_DATA_HOME/perl5/lib/perl5 ]] &&
+ eval $(perl -I$XDG_DATA_HOME/perl5/lib/perl5
+ -Mlocal::lib=$XDG_DATA_HOME/perl5 2>/dev/null)
-# load site-specific
+### load site-specific
if [[ -f ~/.zprofile.local ]] { source ~/.zprofile.local }
# vim: et sts=4 sw=4 ts=8 tw=79