Experimental Physics and
| |||||||||||||||
|
We are using the attached Tcl program (shellbox.tcl) to wrap ioc shells on Linux. It requires the "expect" package. It allows any number of users to connect at the same time using telnet. (Without password, thus better do not run it on an open network.) It also logs shell output. However, at the moment, scrolling back the output is not yet implemented. We use it in combination with the also attached init script (shellbox) to start ioc shells as demons. The init script reads a config file (/etc/shellbox.conf) which looks like this: #shellbox configuration file #Starts commands inside a "box" with a telnet-like server. #Contact the shell with: telnet <hostname> <port> #Syntax: #port user directory command args 50000 zimoch /ioc/TEST-IOC-DIRK iocsh startup.cmd Dirk Rod Nussbaumer wrote: Eric Norum wrote:We have an 'iocConsole' script which ssh's to a common machine as a common user (controlled by ssh public keys) and connects to a screen
#!/usr/bin/tclsh # shellbox # # This program serves as a box that runs a shell in it. # stdout and stdin of the shell are connected to the box. # If the shell uses stderr, you should wrap it in a script # that redirects stderr to stdout. # # Whenever the shell dies, it will be restarted. # # Clients can connect to the box via TCP (e.g. with telnet). # Any number of clients can connect at the same time. Input # of all clients is merged. Output of the shell is broadcasted # to all clients and to stdout of the box. # # When started with the -paranoid option, the box only accepts # connections from localhost. # # The box intercepts the following commands: # exit or quit disconnect from the box # clients gives a list of all connected clients # kill terminates the shell # package req Expect log_user 0 set timeout -1 regsub -all {\$} {shellbox by Dirk Zimoch $Source: /cvs/CVSROOT/UTILITIES/scripts/tcl/shellbox,v $ $Revision: 1.6 $ $Date: 2003/06/26 12:55:49 $} {} version proc usage {} { puts "usage: shellbox \[options\] port command args" puts "options: -help : show this text" puts " -version : show cvs release" puts " -paranoid : allow connections from localhost only" puts " -dir directory : set working directory" } proc createServer {port} { global paranoid if [catch { if $paranoid { socket -server connectionHandler -myaddr localhost $port } else { socket -server connectionHandler $port } } msg] { puts stderr "shellbox: Can't install server on port $port" puts stderr $msg exit 2 } puts "server started on port $port" } proc bgerror {args} { global errorInfo catch {puts stderr "shellbox: $errorInfo"} } set channels {} proc connectionHandler {channel addr port} { global channels command clientinfo set peer [fconfigure $channel -peername] set clientinfo($channel) [lindex $peer 1]:[lindex $peer 2] fconfigure $channel -blocking no -buffering none # do some telnet magic to change from line-mode to char-mode puts -nonewline $channel "\xff\xfb\x03\xff\xfb\x01" fileevent $channel readable "inputHandler $channel" forwardOutput "**** new client $clientinfo($channel) ****\n" catch {puts $channel "**** '$command' running ****"} lappend channels $channel catch {puts $channel "clients:\n[getClientlist]"} send "\n" } proc closeChannel {channel} { global channels clientinfo catch {puts $channel "**** see you later ****"} set index [lsearch $channels $channel] set channels [lreplace $channels $index $index] forwardOutput "**** client $clientinfo($channel) logged out ****\n" close $channel unset clientinfo($channel) } proc inputHandler {channel} { global echo set data [read $channel] if [eof $channel] { closeChannel $channel return } binary scan $data H* hex switch -glob -- $hex { 03 {closeChannel $channel} 04 {closeChannel $channel} 18 {killProgram} fff4fffd06 {closeChannel $channel} ffedfffd06 {closeChannel $channel} ff* {puts "ignored $data"} default {send -- $data} } } proc forwardOutput {data {exclude ""}} { global channels catch {puts -nonewline $data} foreach channel $channels { if {$channel != $exclude} { catch {puts -nonewline $channel $data} } } } proc getClientlist {} { global channels clientinfo set result "" foreach channel $channels { append result "$clientinfo($channel)\n" } return $result } set diedyoung 1 proc startProgram {} { global command spawn_id diedyoung directory # catch {close $spawn_id} if {[catch {cd $directory} msg]} { forwardOutput "**** $msg (trying later) ****\n" while {[catch {cd $directory}]} { after 10000 } } after 1000 set diedyoung 0 if [catch { eval spawn $command } msg] { catch {puts stderr "shellbox: $msg"} exit 3 } forwardOutput "**** '$command' started in [pwd] ****\n" expect_background { kill { killProgram } ? { forwardOutput $expect_out(buffer) } eof { puts "EOF received" if ($diedyoung) { forwardOutput $expect_out(buffer) forwardOutput "**** first run of '$command' died within first 10 seconds. I will quit. ****\n" exit 4 } forwardOutput $expect_out(buffer) forwardOutput "**** '$command' died ****\n" after idle startProgram } } } proc killProgram {} { global command forwardOutput "**** killing '$command' ****\n" exec kill -s SIGKILL [exp_pid] wait forwardOutput "**** '$command' killed ****\n" } set paranoid 0 set directory . while {[string match -* $argv]} { switch -exact -- [lindex $argv 0] { "-?" - "-help" { usage; exit 0 } "-v" - "-version" {puts $version; exit 0} "-paranoid" {set paranoid 1} "-echo" {puts stderr "shellbox: option -echo is obsolete"} "-dir" { set directory [lindex $argv 1] set argv [lrange $argv 2 end] continue } default {puts stderr "shellbox: unknown option [lindex $argv 0]"; usage; exit 1} } set argv [lrange $argv 1 end] } set port [lindex $argv 0] set command [lrange $argv 1 end] if {![regexp {^[0-9]+$} $port] || [llength $command] == 0} { usage; exit 1 } fconfigure stdout -buffering none startProgram createServer $port vwait forever #!/bin/bash # chkconfig: 2345 98 2 # description: shellbox service for spawning programs . /etc/rc.d/init.d/functions export PATH=$PATH:/usr/local/bin . /etc/profile export SLSBASE=/work export INSTBASE=/work export HOSTNAME=$(hostname -s) exe=/usr/local/bin/shellbox.tcl prog=shellbox params= conf=/etc/shellbox.conf logdir=/var/log/$prog shells=/var/run/$prog [ -n "$logdir" -a -d $logdir ] || mkdir $logdir fail () { echo -n $@ failure echo exit 1 } launch () { if [ "$1" = "-reload" ] then reload=YES shift fi temp=$(mktemp -p $(dirname $shells)) || fail "can't create temporary file" while read PORT USER DIR COMMAND do # check for empty lines and comments [[ $PORT == "" || $PORT == \#* ]] && continue # check if already started shell is still alive if LINE=$(grep "$PORT $USER $DIR $COMMAND" $shells 2> /dev/null) then PID=${LINE%% *} if checkpid $PID then if [ -z "$reload" ] && [ -z "$*" ] || echo "$*" | grep -qw $PORT then echo_warning echo "Already running: $PORT $USER $DIR $COMMAND" fi echo $LINE >> $temp continue fi fi # check if we have to start all shells or only this PORT [ "$*" ] && echo "$*" | grep -qwv $PORT && continue if [ -n "$logdir" ] then LOG=$logdir/$PORT rm -f $LOG else LOG=/dev/null fi # start shellbox as other user echo -n Starting: $PORT $USER $DIR $COMMAND export SHELLBOX=$HOSTNAME:$PORT export HOME=$(getent passwd $USER | awk -F : '{print $6}') sudo -u $USER $exe -dir $DIR $params $PORT $COMMAND >> $LOG 2>&1 < /dev/null & PID=$! # check if starting worked or failed sleep 0.1 if checkpid $PID then echo "$PID $PORT $USER $DIR $COMMAND" >> $temp echo else echo_failure echo cat $LOG fi done < $conf mv $temp $shells chmod 0644 $shells } start () { [ -r $conf ] || fail "$conf not readable" [ -x $exe ] || fail "$exe is not executable" launch $* touch /var/lock/subsys/$prog } stopshell() { PID=$1 PORT=$2 shift echo -n Stopping: $* kill $PID 2> /dev/null || echo_failure echo if [ $logdir ] then echo -e "\n**** stopped ****" >> $logdir/$PORT fi } stop () { # anything to stop? if [ ! -r $shells ] then echo -n "$prog: No shells started." success echo exit 0 fi if [ -z "$1" ] then # kill all shellboxes while read PID PORT ARGS do stopshell $PID $PORT $ARGS done < $shells rm -f $shells rm -f /var/lock/subsys/$prog else # kill only selected shellboxes temp=$(mktemp -p $(dirname $shells)) || fail "can't create temporary file" while read PID PORT ARGS do echo "$*" | grep -qw $PORT && stopshell $PID $PORT $ARGS || echo $PID $PORT $ARGS >> $temp done < $shells mv $temp $shells chmod 0644 $shells fi } reload () { echo "Reloading $conf: " [ -r $conf ] || fail "not readable" # anything to stop? if [ -r $shells ] then #first kill all shells that are not configured any more temp=$(mktemp -p $(dirname $shells)) || fail "can't create temporary file" while read PID ARGS do while read PORT USER DIR COMMAND do if [ "$PORT $USER $DIR $COMMAND" = "$ARGS" ] then echo "Keeping: $ARGS" echo "$PID $ARGS" >> $temp continue 2 fi done < $conf stopshell $PID $PORT $ARGS done < $shells mv $temp $shells chmod 0644 $shells fi #now start all new shells launch -reload } status () { [ -r $conf ] || fail "$conf not readable" if [ "$1" = "-log" ] then log=YES shift fi echo -e "pid\tport\tuser\tdir\t\t\tcommand" while read PORT USER DIR CMD do # check for empty lines and comments [[ $PORT == "" || $PORT == \#* ]] && continue # check if we have to report all shells [ "$*" ] && echo "$*" | grep -wqv $PORT && continue if [ "$logdir" -a "$log" ] then echo "-------------------------------------------------------------------" fi if LINE=$(grep "$PORT $USER $DIR $CMD" $shells 2> /dev/null) then PID=${LINE%% *} if checkpid $PID then echo -n $PID else $SETCOLOR_FAILURE echo -n DEAD $SETCOLOR_NORMAL fi else $SETCOLOR_FAILURE echo -n STOPPED $SETCOLOR_NORMAL fi echo -e "\t$PORT\t$USER\t$DIR\t$CMD" if [ "$logdir" -a "$log" ] then grep '\*\*\*\*' $logdir/$PORT 2>/dev/null fi done < $conf } CMD=$1 shift case "$CMD" in (start) start $*;; (stop) stop $*;; (restart) stop $*; start $*;; # kill all shells, then start again (reread|reload) reload $*;; # reload shellbox.conf without killing too much (status) status $*;; (*) echo "Usage: $0 {start [ports]|stop [ports]|restart [ports]|reload|status [-log] [ports]}" ;; esac
| ||||||||||||||
ANJ, 02 Sep 2010 |
·
Home
·
News
·
About
·
Base
·
Modules
·
Extensions
·
Distributions
·
Download
·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing · |