#!/bin/bash # Copyright (C) 2022 Sean Whitton # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # emacsclient(1) wrapper to handle updating the environment of existing # daemons and choosing between installed and in-tree builds of Emacs. min_arg=0 command -v ss >/dev/null || exit 1 socket_dir="/run/user/$(id -u)/emacs/" get_listener () { local socket="$1" local listener=$(ss -Hplx src "$socket" \ | perl -wne'/pid=(\d+)/ and print $1') if [ -z "$listener" -a -e "$socket" ]; then # Nothing is listening: remove dangling socket. rm "$socket" else echo "$listener" fi } for arg do shift case "$arg" in '--spw/installed') mode=--spw/installed ;; '--spw/no-start') start=false ;; '--spw/update-environment') update=true ;; '-s'|'--socket-name') min_arg=2; daemon_name="$1" ;;& '-s'?*) min_arg=1; daemon_name="${arg:2}" ;;& '--socket-name='?*) min_arg=1; daemon_name="${arg:14}" ;;& *) set -- "$@" "$arg" ;; esac done socket="$socket_dir${daemon_name:-server}" listener="$(get_listener $socket)" if [ -n "$listener" ]; then case "$(ps h -o exe $listener | sed -e 's/ (deleted)$//')" in "$HOME/src/emacs/src/emacs") devel_running=true ;; "$(realpath $(command -v emacs))") installed_running=true ;; *) echo >&2 "Unknown Emacs listening on $socket"; exit 1 ;; esac fi if [ "$devel_running" = true -a "$mode" = --spw/installed ]; then kill "$listener" # Detach gdb so that Emacs can handle the SIGTERM. # We also have to quit gdb so that `gdb-inferior-status' is reset from # "signal-received"; see `spw/gdbmacs-attach' command. [ -n "$(get_listener ${socket_dir}gdbmacs)" ] \ && emacsclient -sgdbmacs --eval '(gud-basic-call "detach")' \ --eval '(gud-basic-call "quit")' [ -e "$socket" ] && inotifywait -qqt 2 -e delete "$socket" devel_running=false fi [ "$start" = false -a -z "$listener" ] && exit 0 # We check whether there is something which looks like the installed Emacs # running because we do not want the in-tree emacsclient to try to connect to # that, and we check whether there is a build in progress and no running # daemon, as we don't want to start a new daemon until the build is finished. # The idea is that I can always hit C-i e without worrying about builds. if [ "$mode" != --spw/installed -a "$installed_running" != true \ -a -e "$HOME/src/emacs/admin/flagfile.melete" \ -a -x "$HOME/src/emacs/lib-src/emacsclient" \ -a -d "/usr/share/emacs-snapshot/site-lisp/elpa" ] \ && ! ( [ "$devel_running" != true ] && ps h -o pid -C make \ | xargs pwdx | grep -q "$HOME/src/emacs" ); then emacs="$HOME/src/emacs/src/emacs" emacsclient="$HOME/src/emacs/lib-src/emacsclient" else emacs=$(command -v emacs) emacsclient=$(PATH=$(echo "$PATH" | sed -e "s#$HOME/src/dotfiles/bin:##") \ command -v emacsclient) fi # Update Emacs daemon environment vars for a new interactive session. This # allows us to have the Emacs daemon survive restarts of the graphical desktop # environment, and to update Emacs on a remote host when there is a new # forwarded SSH agent socket. See 'upenv' in .bash_defns. if [ "$update" = true -a -n "$listener" ]; then for var in DISPLAY WAYLAND_DISPLAY \ XAUTHORITY WINDOWID \ XDG_SESSION_TYPE XDG_CURRENT_DESKTOP \ SWAYSOCK I3SOCK \ SSH_ASKPASS SSH_CONNECTION \ SSH_AUTH_SOCK SSH_AGENT_PID; do eval isset=\${$var+x} if [ "$isset" = "x" ]; then eval val=\$$var args="$args"' "'$var'" "'$val'"' fi done [ -n "$args" ] \ && "$emacsclient" -s"${daemon_name:-server}" \ --eval "(spw/update-environment$args))" fi # emacsclient(1) requires an argument. As a special case, if there are no # arguments except -s/--socket-name, and no daemon is running, just start one. # And if there were no arguments but we were requested just to update env # vars, exit peacefully. if [ "$#" -eq "$min_arg" -a -z "$listener" ]; then exec "$emacs" --daemon"${daemon_name:+=$daemon_name}" elif ! [ "$#" -eq "$min_arg" -a "$update" = true ]; then exec "$emacsclient" -a "" "$@" fi