Remoteusage without password-free login requirement
This is alternative to remoteusage where password-free login is not a requirement. See remoteusage page for other requirements and general information.
This solution uses one pre-made ssh connection where the client is put into "master" mode (-M) for connection sharing. The wrapper script then uses the control socket created by this pre-made ssh connection for its own connection. As long as master ssh connection is live, slave can use it. Disconnecting master all future attempts to connect from the script will fail.
It is possible to use this solution without any changes to
$HOME/.ssh/config
.
At the end of this document there is information for some possible ways how master ssh connection can be done.
The script
Write the following code to a file, for example remote-notmuch.sh
.
#!/bin/bash
set -euf
# To trace execution, uncomment next line:
#exec 6>>remote-errors; BASH_XTRACEFD=6; echo -- >&6; set -x
: ${REMOTE_NOTMUCH_SSHCTRL_SOCK:=master-notmuch@remote:22}
: ${REMOTE_NOTMUCH_COMMAND:=notmuch}
readonly REMOTE_NOTMUCH_SSHCTRL_SOCK REMOTE_NOTMUCH_COMMAND
SSH_CONTROL_ARGS='-oControlMaster=no -S ~'/.ssh/$REMOTE_NOTMUCH_SSHCTRL_SOCK
readonly SSH_CONTROL_ARGS
printf -v ARGS '%q ' "$@" # bash feature
readonly ARGS
if ssh -q $SSH_CONTROL_ARGS 0.1 "$REMOTE_NOTMUCH_COMMAND" $ARGS
then exit 0
else ev=$?
fi
# continuing here in case ssh exited with nonzero value
case $* in
'config get user.primary_email') echo 'nobody@nowhere.invalid'; exit 0 ;;
'config get user.name') echo 'nobody'; exit 0 ;;
'count'*'--batch'*) while read line; do echo 1; done; exit 0 ;;
'count'*) echo 1; exit 0 ;;
'search-tags'*) echo 'errors'; exit 0 ;;
'search'*'--output=tags'*) echo 'errors'; exit 0 ;;
esac
# fallback exit handler; print only to stderr...
exec >&2
if ssh $SSH_CONTROL_ARGS -O check 0.1
then
echo " Control socket is alive but something exited with status $ev"
exit $ev
fi
echo " See`sed '1d;2d;s/.//;q' "$0"` for help"
exit $ev
#eof
Note the 0.1
in ssh command line. It is used to avoid any opportunistic
behaviour ssh might do; for example if control socket is not alive ssh
would attempt to do its own ssh connection to remote ssh server. As
address 0.1
is invalid this attempt will fail early.
Test
Easiest way to test this script is to run the pre-made ssh connection using the following command line:
ssh -M -S '~'/.ssh/master-notmuch@remote:22 [user@]remotehost sleep 600
(replace [user@]remotehost
with your login info). Doing this the
above wrapper script can be run unmodified. After the above command has
been run on one terminal, enter chmod +x remote-notmuch.sh
in
another terminal and then test the script with
./remote-notmuch.sh help
Note that the '~' in the ssh command line above is inside single quotes
for a reason. In this case shell never expand it to $HOME
-- ssh does
it by not reading $HOME
but checking the real user home directory
from /etc/passwd
. For security purposes this is just how it should
be.
Tune
The path '~'/.ssh/master-notmuch@remote:22
might look too generic to be
used as is as the control socket after initial testing (but it can
be used). It is presented as a template for what could be configured
to $HOME/.ssh/config
. For example:
Host *
ControlPath ~/.ssh/master-%h@%p:%r
is a good entry to be written in $HOME/.ssh/config
;
remoteusage uses the same. Now, let's say you'd
make your pre-made ssh connection with command
ssh -M robin@example.org
There are 3 options how to handle this with ./nottoomuch-remote.bash
:
1) Edit ./nottoomuch-remote.bash
and change REMOTE_NOTMUCH_SSHCTRL_SOCK
to contain the new value (being master-robin@example.org:22 in this
case)
2) Make symlink:
ln -sfT master-robin@example.org:22 ~/.ssh/master-notmuch@remote:22
3) REMOTE_NOTMUCH_SSHCTRL_SOCK
can be used via environment; like:
REMOTE_NOTMUCH_SSHCTRL_SOCK=master-robin@example.org:22 ./nottoomuch-remote.bash help
Configure Emacs on the client computer
Add something like the following functions to your Emacs (general(*) or notmuch specific) configuration files:
;; this should work as backend function when copied verbatim
(defun user/notmuch-remote-setup (sockname)
(setq notmuch-command "/path/to/nottoomuch-remote.bash")
(setenv "REMOTE_NOTMUCH_SSHCTRL_SOCK" sockname)
;; If you use Fcc, you may want to do something like this on the client,
;; to Bcc mails to yourself (if not, remove in your implementation):
(setq notmuch-fcc-dirs nil)
(add-hook 'message-header-setup-hook
(lambda () (insert (format "Bcc: %s <%s>\n"
(notmuch-user-name)
(notmuch-user-primary-email))))))
;; this is just an example to configure using "default" master socket
(defun user/notmuch-remote-default ()
(interactive)
(user/notmuch-remote-setup "master-notmuch@remote:22")
;; usage example2: set USER & HOST1 according to your remote...
(defun user/notmuch-remote-at-HOST1 ()
(interactive)
(user/notmuch-remote-setup "master-USER@HOST1:22")
;; ... you probably got the point now -- add relevant funcs to your config
(defun user/notmuch-remote-at-HOST2 ()
(interactive)
(user/notmuch-remote-setup "master-USER@HOST2:22")
... and if you want to activate your remote by default just call
(user/notmuch-remote-setup "master-USER@HOST:22")
without function call
wrapper.
(*) general most likely being ~/.emacs
Yet another possibility -- script to start emacs
Instead of adding new configurations you could also write a special
script which starts and configures emacs suitable for remote usage.
Copy the following "template" to a new name e.g. in $HOME/bin/
,
edit the value for master-USER@HOST3:22
and perhaps add more
post eval-after-load notmuch configs there.
#!/bin/sh
:; exec "${EMACS:-emacs}" --debug-init --load "$0" "$@"; exit
(setenv "REMOTE_NOTMUCH_SSHCTRL_SOCK" "master-USER@HOST3:22")
(eval-after-load "notmuch"
(lambda ()
(setq notmuch-command (concat (file-name-directory load-file-name)
"remote-notmuch.sh"))
;; place for more post-notmuch-load emacs configs if any
))
(notmuch)
;; Local Variables:
;; mode: emacs-lisp
;; End:
Creating master connection
(Note: all the examples below use the default master socket written in
./nottoomuch-remote.bash
for initial test easiness; remove/change the
-S '~'/.ssh/master-notmuch@remote:22
in case you don't need it.)
As mentioned so many times, using this solution requires one pre-made ssh connection in "master" mode. The simplest way is to dedicate one terminal for the connection with shell access to the remote machine:
ssh -M -S '~'/.ssh/master-notmuch@remote:22 [user@]remotehost
One possibility is to have this dedicated terminal in a way that the connection has (for example 1 hour) timeout:
ssh -M -S '~'/.ssh/master-notmuch@remote:22 [user@]remotehost sleep 3600
The above holds the terminal. The next alternative puts the command in background:
ssh -f -M -S '~'/.ssh/master-notmuch@remote:22 [user@]remotehost sleep 3600
If you don't want this to timeout so soon, use a longer sleep, like 99999999 (8 9:s, 1157 days, a bit more than 3 years).
A more "exotic" solution would be to make a shell script running on remote machine, checking/inotifying when new mail arrives. When mail arrives it could send message back to local host, where a graphical client (to be written) pops up on display providing info about received mail (and exiting this graphical client connection to remote host is terminated).
Troubleshooting
If you experience strange output when using from emacs first attempt to just run
./remote-notmuch.sh help
from command line and observe output. If it looks as it should be next uncomment the line
#exec 6>>remote-errors; BASH_XTRACEFD=6; echo -- >&6; set -x
in ./remote-notmuch.sh
and attempt to use it from emacs again -- and then
examine the contents of remote-errors
in the working directory emacs was
started.