Commit a21c78b7 authored by Jameson Rollins's avatar Jameson Rollins

improved guardctrl remote interface

All guardctrl handling moved into the python module (both local and remote).

Use json encoding to pass arguments to SSH_ORIGINAL_COMMAND, which is a much
more robust way of handling argument spaces, and unbreaks various option
handling.

The guardlog command is re-purposed to exclusively handle opening logs in
an xterm.
parent 5c48c39e
Pipeline #47047 passed with stages
in 37 seconds
#!/bin/bash
##
## guardctrl client wrapper
##
GUARDCTRL_USER=${GUARDCTRL_USER:=guardian}
GUARDCTRL_HOST=${GUARDCTRL_HOST:="${IFO,,*}guardian"}
# as backwards-compatible convenience, if executed as 'guardlog' set
# command to be 'log'
if [[ $(basename "$0") == 'guardlog' ]] ; then
cmd=log
fi
##################################################
# if ~/.guardctrl-home file exists then this is the guardctrl home and
# therefore just execute guardctrl directly
if [ -f ~/.guardctrl-home ] || [ "$GUARDCTRL_LOCAL" ] ; then
exec python3 -u -m guardctrl $cmd "$@"
fi
# FIXME: would be preferable to reverse this logic and only execute
# ssh if GUARDCTRL_USER@GUARDCTRL_HOST is set, but for convenience we
# want to ship "defaults" for GUARDCTRL_{USER,HOST} so we don't have
# to set it site wide. Can we just force sites to set it?
##################################################
# remote client. assumes guardctrl is setup as ssh ForceCommand for
# GUARDCTRL_USER@GUARDCTRL_HOST
# echo "connecting to guardctrl host: $GUARDCTRL_USER@$GUARDCTRL_HOST..." >&2
# force TTY by default, with "+t" option to unset and allow separate stdout/stderr streams
TTY=-t
if [[ "$1" == '+t' ]] ; then
unset TTY
shift
fi
# we setup a master connection and use ControlPersist to hold it open
# for a while for faster subsequent calls
mkdir -p ~/.ssh/controls/
SSH_CMD="ssh -q \
-S ~/.ssh/controls/%r@%h:%p \
-o ControlMaster=auto \
-o ControlPersist=600 \
$TTY \
${GUARDCTRL_USER}@${GUARDCTRL_HOST} \
--"
# launch in xterm
if [[ "$1" == '-x' ]] ; then
shift
xterm -si -sk \
-geometry 150x80 \
-bg DodgerBlue4 -fg white \
-fa Monospace -fs 10 \
-title "guardctrl: $*" \
-e $SSH_CMD $cmd "$@" \
&
else
exec $SSH_CMD $cmd "$@"
fi
#!/bin/sh
exec python3 -u -m guardctrl "$@"
guardctrl
\ No newline at end of file
#!/bin/bash -x
exec xterm \
-hold \
-si -sk +sb \
-geometry 150x80 \
-bg DodgerBlue4 \
-fg white \
-fa Monospace \
-fs 10 \
-title "guardctrl: $*" \
-e "python3 -u -m guardctrl log -f $*" \
&
from __future__ import print_function
import os
import sys
import json
import signal
import logging
import argparse
......@@ -310,7 +312,7 @@ Examples:
""".format(prog=PROG+' log')
p.formatter_class = argparse.RawDescriptionHelpFormatter
p.add_argument('+f', '--no-follow', dest='follow', action='store_false',
p.add_argument('-f', '--follow', action='store_true',
help="dump logs and do not follow")
p.add_argument('-a', '--after', metavar='DATETIME', type=datetime,
help="filter for logs after specified date/time")
......@@ -353,13 +355,16 @@ subparser.add_parser('journalctl')
##################################################
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
def main_local():
# handle invocation as ssh ForceCommand
if os.getenv('SSH_ORIGINAL_COMMAND') and len(sys.argv) == 1:
sys.argv += os.getenv('SSH_ORIGINAL_COMMAND').split()
ssh_args = os.getenv('SSH_ORIGINAL_COMMAND')
if ssh_args and len(sys.argv) == 1:
# assume json argument encoding if the first character is the
# json bracket list start character
if ssh_args[0] == '[':
sys.argv += json.loads(ssh_args)
else:
sys.argv += os.getenv('SSH_ORIGINAL_COMMAND').split()
# parse known args first, just to get debug flag and handle direct
# sys commands
......@@ -383,5 +388,59 @@ def main():
args.func(args)
def main_remote():
parser = argparse.ArgumentParser(prefix_chars='-+', add_help=False)
parser.add_argument('+t', dest='terminal', action='store_false')
wargs, args = parser.parse_known_args()
IFO = os.getenv('IFO')
GUARDCTRL_USER = os.getenv('GUARDCTRL_USER', 'guardian')
GUARDCTRL_HOST = os.getenv('GUARDCTRL_HOST', '{}guardian'.format(IFO.lower()))
SSH_CMD = [
'ssh',
'-q',
'-S', '~/.ssh/controls/%r@%h:%p',
'-o', 'ControlMaster=auto',
'-o', 'ControlPersist=600',
]
# force TTY by default, with "+t" option to unset and allow
# separate stdout/stderr streams
if wargs.terminal:
SSH_CMD += ['-t']
SSH_CMD += [
'{}@{}'.format(GUARDCTRL_USER, GUARDCTRL_HOST),
'--',
]
# create a directory for the ssh control sockets
try:
os.makedirs('~/.ssh/controls/')
except:
pass
# json encode commands for ssh, to pass spaces in a reasonable way
args_enc = json.dumps(args)
# launch in foreground
subprocess.call(SSH_CMD + [args_enc])
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
if os.path.exists('~/.guardctrl-home') or os.getenv('GUARDCTRL_LOCAL'):
main_local()
elif os.getenv('GUARDCTRL_HOST'):
main_remote()
else:
main_local()
if __name__ == '__main__':
main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment