conmux-0.12.0/0000755000175000017500000000000011376624514011610 5ustar loolloolconmux-0.12.0/start0000755000175000017500000000513311376624514012675 0ustar loollool#! /bin/sh # # start -- start up configured conmux servers on this host. # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # if [ -f ~/.gmm.conf ]; then . ~/.gmm.conf fi CONMUX=${CONMUX:-/usr/local/conmux} cmd="start" if [ "$1" != "" ]; then cmd="$1" fi PATH=$CONMUX/bin:$CONMUX/sbin:$CONMUX/lib/drivers:$CONMUX/lib/helpers:$PATH function start() { typeset name="$1" typeset pf="$CONMUX/log/$name.pid" shift # Determine whether it is already running ... if so leave it be. if [ -f "$pf" ]; then if kill -0 `cat "$pf"` 2>/dev/null; then return 1 fi fi echo "starting $name ..." "$@" >"$CONMUX/log/$name.log" 2>&1 & echo "$!" >"$pf" return 0 } function stop() { typeset name="$1" typeset pf="$CONMUX/log/$name.pid" echo "stopping $name ..." # Kill it and clear up kill -HUP `cat "$pf"` 2>/dev/null rm -f "$pf" } existing="" for i in $CONMUX/log/*.pid do n=${i%.pid} n=${n#$CONMUX/log/} if [ "$n" != "*" ]; then existing="$existing $n" fi done if [ "$cmd" = "start" ]; then autoboot="" [ -f $CONMUX/etc/registry ] || touch $CONMUX/etc/registry start registry $CONMUX/sbin/conmux-registry 63000 $CONMUX/etc/registry if [ "$?" -eq 0 ]; then sleep 1 fi started="registry" pause=0 for i in $CONMUX/etc/*.cf do n=${i%.cf} n=${n#$CONMUX/etc/} if [ "$n" != "*" ]; then if [ -f "$CONMUX/log/$n.cf" ]; then if ! cmp -s "$i" "$CONMUX/log/$n.cf"; then stop $n fi fi start $n $CONMUX/sbin/conmux $i if [ "$?" -eq 0 ]; then pause=1 fi started="$started $n" # Preserve the orginal configuration file. cp "$i" "$CONMUX/log/$n.cf" if grep -q TYPE:numaq "$i"; then autoboot="$autoboot $n" fi for i in `grep FLAGS: "$i"`; do case "$i" in \#|FLAGS:) ;; *) autoboot="$autoboot $n/$i" ;; esac done fi done if [ "$pause" -eq 1 ]; then sleep 1 fi for nh in $autoboot do name="${nh%/*}" helper="${nh#*/}" mn="${name#abat-}" start $name-$helper-helper $CONMUX/bin/conmux-attach $mn $helper-helper started="$started $name-$helper-helper" done fi if [ "$cmd" = "start" -o "$cmd" = "stop" ]; then for i in $existing do case " $started " in *\ $i\ *) ;; *) stop "$i" ;; esac done fi if [ "$cmd" = "status" ]; then for n in $existing do mn="${n#abat-}" case "$n" in registry|*-helper) ;; *) status=`console -s $mn` echo "$mn $status" esac done fi conmux-0.12.0/conmux.html0000644000175000017500000001411011376624514014004 0ustar loollool conmux - the console multiplexor

conmux - the console multiplexor

conmux, the console multiplexor is a system designed to abstract the concept of a console. That is to provide a virtualised machine interface, including access to the console and the 'switches' on the front panel; the /dev/console stream and the reset button. It creates the concept of a virtual console server for multiple consoles and provides access to and sharing of consoles connected to it.

There are two main motivations for wanting to do this. Firstly, we have many different machine types with vastly differing access methodologies for their consoles and for control functions (VCS, HMC, Annex) and we neither want to know what they are nor how they function. Secondly, most console sources are single access only and we would like to be able to share the console data between many consumers including users.

Basic Usage

The main interface to the consoles is via the console program. This connects us to the console server for the machine and allows us to interact with it, including issuing out-of-band commands to control the machine.

$ console <host>/<console>

In the example below we indicate that the console we require is located on the virtual console server consoles.here.com and the specific console is elm3b70.

$ console consoles.here.com/elm3b70 Connected to elm3b70 console (~$quit to exit) Debian GNU/Linux 3.1 elm3b70 ttyS0 elm3b70 login:

Once connected we can interact normally with the console stream. To perform front pannel operation such as peforming an hard reset we switch to command mode. This is achieved using the escape sequence ~$. Nore the prompt Command>

elm3b70 login: ~$ Command> quit Connection closed $

Command Summary

The following commands are generally available:

CommandDescription
quitquit this console session, note that this disconnects us from the session it does not affect the integity of the session itself.
hardresetforce a hard reset on the machine, this may be a simple reset or a power off/on sequence whatever is required by this system.

Architecture

The conmux provides a virtual console multiplexor system reminicent of an Annex terminal server. You refer to the conmux server and lines, unlike an Annex lines are referred to by mnemonic names. Above we referred to the console for elm3b70 'connected to' the server consoles.here.com. A virtual console server consists of a number of server processes. One conmux-registry server, several conmux servers and optionally several helper processes.

conmux-registry: a server is defined by the server registry. This maintains the mnemonic name to current server location relation. When a client wishes to attach to a console on a server, the registry is first queried to locate the server currently handling that console.

conmux: for each connected console there is a corresponding console multiplexor. This process is responsible for maintaining the connection to the console and for redistributing the output to the various connected clients. It is also responsible for handling "panel" commands from the client channels.

autoboot-helper: an example helper which aids systems which are not capable of an automatic reboot. It connects to a console and watches for tell-tale reboot activity, preforming a "panel" hardreset when required. This provides the impression of seamless reboot for systems which this does not work.

Configuration

conmux-registry

Configuration of this service is very simple. Supplying the default registry port (normally 63000) and the location for the persistant registry database.

conmux

Configuration of each conmux is complex. Each has a listener, payload and optionally one or more panel commands. Configuration is provided via a per console configuration file. This file consists of lines defining each element:

listener <server>/<name>: defines the name of this console port as it appears in the registry.

socket <name> <title> <host>:<port>: defines a console payload connected to a tcp socket on the network. name defines this payload within the multiplexor, title is announced to the connecting clients.

application <name> <title> <cmd>: defines a console payload which is accessed by running a specific command. name defines this payload within the multiplexor, title is announced to the connecting clients.

command <panel> <message> <cmd>: defines a panel command for the preceeding payload, triggerd when panel is typed at the command prompt. message is announced to the user community. cmd will be actually executed.

For example here is the configuration for a NUMA-Q system which is rebooted using a remote VCS console and for which the real console channel is on an Annex terminal server:

listener localhost/elm3b130 socket console 'elm3b130 console' console.server.here.com:2040 command 'hardreset' 'initated a hard reset' \ './reboot-numaq vcs 1.2.3.4 elm3b130 12346 Administrator password'
conmux-0.12.0/drivers/0000755000175000017500000000000011376624514013266 5ustar loolloolconmux-0.12.0/drivers/reboot-apc0000755000175000017500000000355011376624514015252 0ustar loollool#!/usr/bin/expect -- # # Reboot a machine connected to an APC power strip # # Copyright 2007 Google Inc., Martin J. Bligh set P "reboot-apc" # # OPTIONS: options parser. # proc shift {_list} { upvar $_list list set res [lindex $list 0] set list [lreplace $list 0 0] return $res } proc arg {_list arg} { upvar $_list list if {[llength $list] < 1} { puts stderr "$arg: required argument missing" exit 1 } return [shift list] } set timeout 10 set user {apc} set pass {apc} set host [lindex $argv 0] set outlet [lindex $argv 1] shift argv shift argv while {[llength $argv] > 0} { puts "length [llength $argv]" switch -- [shift argv] { -p { set pass [arg argv p]} -u { set user [arg argv u]} } } if {[llength $argv] > 0} { puts stderr "Usage: $P [-u ] [-p ]" exit 1 } if {[string compare $host ""] == 0 || [string compare $outlet ""] == 0} \ { puts stderr "host and outlet required" exit 1 } spawn telnet $host expect "User Name :" send $user send "\r" expect "Password :" send $pass send "\r" expect "1- Device Manager" expect "> " send "1\r" # We get a different prompt if we're just an outlet controller # decide which response we need to enter set timeout 2 expect { "3- Outlet Control/Configuration" { send "3\r" exp_continue } "2- Outlet Control" { send "2\r" exp_continue } } send "\r" expect "> " send $outlet send "\r" # Here too, if we're just an outlet controller we don't get the option # to modify configuration expect { "1- Control Outlet" { send "1\r" } } expect "3- Immediate Reboot" expect "> " send "3\r" expect "Immediate Reboot" expect "Enter 'YES' to continue or to cancel :" send "YES\r" expect "Press to continue..." conmux-0.12.0/drivers/reboot-numaq0000755000175000017500000001037311376624514015631 0ustar loollool#!/usr/bin/expect -- # # reboot-numaq -- reboot Numa-Q systems connected to a VCS console # # Use the remote telnet managment interface on the VCS console to # hardreset systems. Systems are identified by their system names # within VCS. # # usage: # reboot-numaq # # examples: # reboot-numaq vcs 1.2.3.4 hummer 12346 FOO BAR # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # set P "reboot-numaq" if { [llength $argv] != 6 } { puts stderr "Usage: reboot-numaq vcs " exit 1 } log_user 0 #stty echo set argc [llength $argv] set console_ip [lindex $argv 1] set system [lindex $argv 2] set console_port [lindex $argv 3] set username [lindex $argv 4] set password [lindex $argv 5] #log_file -a "$logfile" set elapsed_time 0 set timeout 30 set command "telnet $console_ip $console_port" proc note {m} { global P puts "$P: $m" } proc warn {m} { global P puts "$P: WARNING: $m" } proc winge {m} { global P puts "$P: MACHINE ERROR: reboot failed - $m" } # CONNECT: connect to the remote console. note "Logging into VCS console with command \"$command\" to restart it" eval spawn $command expect { default { winge "login prompt not issued" exit 2 } "Connection closed by foreign host." { winge "Telnet connection closed." exit 1 } "Unable to connect to remote host:" { winge "Connection to remote console failed"; exit 2 } "login:" { note "saw login prompt" } } # AUTHENTICATE: send username and password at the relevant prompts note "sending login ..." send -- "$username\r" expect { default { winge "password prompt not issued" exit 2 } "password:" { note "password prompt found" } } note "sending password ..." send -- "$password\r" expect { default { winge "command prompt not issued" exit 2 } "Authentication Failed" { winge "login/password incorrect ... aborting" exit 1 } -- "->" { #note "command prompt found" } } # SYSTEM: try and select the system, if the specified system does not # exist get a listing and print that out whilst we are connected. note "selecting system '$system' ..." send "cd $system\r" set found 1 expect { default { winge "command prompt not issued" exit 2 } -re "cd: path '.*' not found" { winge "system not defined" set found 0 exp_continue } -- "->" { #note "command prompt found" } } proc list_systems {} { note "Defined systems:" send "sysdef -l\r" expect { default { winge "command prompt not issued" exit 2 } -re "sysdef -l\r\n$" { exp_continue } -- "->" { #note "command prompt found" } "\n$" { puts -nonewline "$expect_out(buffer)" exp_continue } } note "complete" } # The system the user specified was not found, give them a hand by # getting a list of systems. if {$found == 0} { list_systems note "complete ... exiting" send "exit\r" exit 1 } # DOWN: shut the system down ... hard. Expect to see nothing before the # prompt in the case of success. set timeout 65 set fail 1 for {set retry 1} {$fail == 1 && $retry <= 3} {incr retry} { note "powering off the system ..." send "power -f -t 60000 off\r" set fail 0 expect { default { winge "command prompt not issued" exit 2 } -- "->" { #note "command prompt found" } "*E*" { warn "power off failed ... attempt $retry" puts -nonewline "$expect_out(buffer)" set fail 1 exp_continue } } } if {$fail == 1} { winge "power off failed ... system not rebooted" list_systems exit 2 } set timeout 30 # UP: power the system on ... and then ask it to boot. note "powering on the system ..." send "power on\r" set fail 0 expect { default { winge "command prompt not issued" exit 2 } -- "->" { #note "command prompt found" } "*E*" { warn "power on failed ..." set fail 1 exp_continue } } if {$fail == 1} { winge "power on failed ... system not rebooted" list_systems exit 2 } note "booting the system ..." send "boot\r" expect { default { winge "command prompt not issued" exit 2 } -- "->" { #note "command prompt found" } "*E*" { warn "boot failed ..." set fail 1 exp_continue } } note "complete ... exiting" send "exit" exit 0 conmux-0.12.0/drivers/blade0000755000175000017500000001163611376624514014272 0ustar loollool#!/usr/bin/expect -- # # blade -- console and management control for IBM blade servers. # # Obtain consoles for and hard reset support for blades in # IBM blade centres. Blades are identified by their blade centre ids. # # usage: # blade reboot|console ... # # example: # blade reboot blade[4] FOO BAR telnet 1.2.3.4 # blade console blade[4] FOO BAR telnet 1.2.3.4 # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # set P "blade" if { [llength $argv] < 5 } { puts stderr "Usage: $P " exit 1 } log_user 0 set cmd [lindex $argv 0] set system [lindex $argv 1] set username [lindex $argv 2] set password [lindex $argv 3] set command [lrange $argv 4 end] # Validate the command. switch -- $cmd { console {} reboot {} default { puts stderr "$P: $cmd: unrecognised command"; exit 1 } } # Ensure the blade name is fully qualified. This ensures that # the console prompt detection as reliable as possible. Add the system # prefix if it was not specified. switch -re -- "$system" { system:.* { } .* { set system "system:$system" } } #log_file -a "$logfile" set elapsed_time 0 set timeout 30 proc note {m} { global P puts "$P: $m" } proc warn {m} { global P puts "$P: WARNING: $m" } proc winge {m} { global P puts "$P: ERROR: $m" } note "Logging into blade centre with command \"$command\" to restart it"; # CONNECT: connect to the remote console. eval spawn $command expect { default { winge "login prompt not issued" exit 2 } "Connection closed by foreign host." { winge "Telnet connection closed." exit 1 } "Unable to connect to remote host:" { winge "Connection to remote console failed" exit 2 } "username:" { #note "saw login prompt" } } # AUTHENTICATE: send username and password at the relevant prompts note "sending login ..." send -- "$username\r" expect { default { winge "password prompt not issued" exit 2 } "password:" { #note "password prompt found" } } note "sending password ..." send -- "$password\r" expect { default { winge "command prompt not issued" exit 2 } "Invalid login!" { winge "login/password incorrect ... aborting" exit 1 } -- "system>" { #note "command prompt found" } } # SYSTEM: first validate the system specifier, if it does not # exist get a listing and print that out whilst we are connected. note "selecting system '$system' ..." send "env -T $system\r" set found 0 set prompt "$system>" expect { default { winge "command prompt not issued" exit 2 } OK { set found 1 exp_continue } -- "system>" { #note "command prompt found" } -ex $prompt { #note "command prompt found" } } # The system the user specified was not found, give them a hand by # getting a list of systems. if {$found == 0} { note "Defined systems:" send "list -l 2\r" expect { default { winge "command prompt not issued" exit 2 } -- "system>" { #note "command prompt found" } "\n$" { puts -nonewline "$expect_out(buffer)" exp_continue } } note "complete ... exiting" send "exit\r" exit 1 } # CONSOLE: open a console channel if {[string compare $cmd "console"] == 0} { set ts_start 1 set ts_cur 1 set retry 0; note "connecting to console ..." # Unless console session lasts for more than two seconds, retry every # thirty seconds for up to a day. This prevents repeated login events # which fill logs and cause loss of useful event log information. while {($ts_cur - $ts_start < 2) && ($retry < 2880)} { # 30 second delay between attempts, except for the first one if {$ts_start != 1} { note "connection failed, retrying after 30 second delay" after 30000 } set ts_start [timestamp]; send "console -o\r" interact { -o -ex $prompt { return } } set ts_cur [timestamp]; incr retry } winge "console lost" exit 0 } # Function to wait until the issued power command has taken effect. proc powerWait {s} { global prompt set ok 0 for {set retry 1} {$ok == 0 && $retry < 30} {incr retry} { note "checking for power state $s ..." after 2000 send "power -state\r" expect { default { winge "command prompt not issued" exit 2 } -ex $s { set ok 1 exp_continue } -ex "$prompt" { #note "command prompt found" } } } if {$ok == 0} { winge "system did not enter power $s state ... aborting" exit 2 } } # DOWN: shut the system down ... hard. Expect to OK before the # prompt in the case of success. note "powering cycling the system ..." send "power -cycle\r" set fail 1 expect { default { winge "command prompt not issued" exit 2 } "OK" { set fail 0 exp_continue } -ex "$prompt" { #note "command prompt found" } } if {$fail == 1} { winge "power cycle failed ... system not rebooted" exit 2 } powerWait On note "complete ... exiting" send "exit" exit 0 conmux-0.12.0/drivers/module.mk0000644000175000017500000000105511376624514015105 0ustar loollool# (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 DRIVERS:=blade hmc reboot-netfinity reboot-newisys reboot-numaq \ reboot-rsa reboot-rsa2 zseries-console x3270_glue.expect \ reboot-acs48 reboot-apc reboot-laurel install:: @[ -d $(BASE)/lib/drivers ] || mkdir $(BASE)/lib/drivers for f in $(DRIVERS); do \ rm -f $(BASE)/lib/drivers/$$f; \ cp -p drivers/$$f $(BASE)/lib/drivers/$$f; \ chmod 755 $(BASE)/lib/drivers/$$f; \ done conmux-0.12.0/drivers/zseries-console0000755000175000017500000002200311376624514016335 0ustar loollool#!/usr/bin/expect # Copyright 2000, 2001, 2004 by Paul Mattes. # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, # provided that the above copyright notice appear in all copies and that # both that copyright notice and this permission notice appear in # supporting documentation. # # s3270 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 file LICENSE for more details. # # Based on cms_cmd.expect licenced as above, modifications generating # zseries-console: # (C) Copyright IBM Corp. 2004, 2005, 2006 # and released under the GNU Public License V2 # # Read in the glue functions. set lib [file dirname $argv0] source "$lib/x3270_glue.expect" set P "console-zseries" if { [llength $argv] != 4 } { puts stderr "Usage: $P zseries " exit 1 } set username [lindex $argv 0] set password [lindex $argv 1] set hostname [lindex $argv 3] set command "ipllnx" # Procedure to wait for a READ prompt from CMS or CP. proc waitread {} { Snap Save while {[Snap Ascii [expr [Snap Rows]-1] [expr [Snap Cols]-17] 4] != "Read"} { Snap Wait Output } } # Procedure to check for the CMS "Ready" prompt. # Returns its row number, or -1 for "MORE..." state, and leaves a screen with # data to read in the Snap buffer. proc cmd_done {} { global verbose Snap Save while {1} { if {[Snap Ascii [expr [Snap Rows]-1] [expr [Snap Cols]-20] 7] == "More..."} { if {$verbose} {puts "More..."} return -1 } set i [expr [Snap Rows]-2] while {$i >= 0} { set text [Snap Ascii $i 0 [Snap Cols]] switch -regexp -- $text { "Ready; T=.*" {return $i} "Ready\(.*\); T=.*" { error [Snap Ascii [expr $i-1] 0 \ [Snap Cols]] } "^ *\$" {} "00: Please choose*" { return $i } default { if {$verbose} { #puts "Incomplete $i '[string trimright $text]'" } set mylength [string length [string trimright $text]] if { $mylength > 5 } { if {[Snap Ascii $i [expr $mylength-6] 6] == "login:"} { puts "Hohooooooooo login prompt found" return $i } } set i 0 } } incr i -1 } Snap Wait Output } } # Execute a command, return the output. proc cms_cmd {text} { global verbose # Clear the screen. Clear # Send the command. String "$text\n" # 'first' is the row where the first line of output will appear. For # the first screenful it's 1; after that it's 0. set first 1 # r is the result. set r {} while {1} { # Wait for a screenful. set d [cmd_done] # Dump out what's there. set i $first #set first 0 if {$d < 0} {set last [expr [Snap Rows]-2]} {set last $d} while {$i <= $last} { set r [linsert $r end [string trimright [Snap Ascii $i 0 [Snap Cols]]]] incr i } if {$d >= 0} { for {set n 0} {$n < [llength $r]} {incr n} { puts [lindex $r $n] } break } # Clear the screen and go around again. Clear } return $r } ## Calculates how many lines are there in the s3270 screen buffer proc find_last_line {} { global debug global verbose Snap Save if {$debug == 1} { puts "v---" foreach l [Snap Ascii] { puts $l } puts "^---" } if {[Snap Ascii [expr [Snap Rows]-1] [expr [Snap Cols]-20] 7] == "More..."} { ## To denote the console buffer is full return -1 } set i [expr [Snap Rows]-3] while {$i >= 0} { set text [Snap Ascii $i 0 [Snap Cols]] switch -regexp -- $text { "^ *\$" { } default { return $i } } incr i -1 } ## To denote the console buffer is empty return -2 } # Print the s3270 screen buffer proc print_cons_buf {} { ##puts "<>" set output 0 while {1} { # Grab the screen and clear it out. Snap Save Clear set screen [Snap Ascii 0 0 [expr [Snap Rows]-3] [Snap Cols]] # Find how many lines are populated and output those. for {set last [llength $screen]} {$last >= 0} {incr last -1} { switch -regexp -- [lindex $screen $last] { "^ *\$" { } default { break } } } ##puts "last<$last>" if {$last < 0} { break } for {set line 0} {$line <= $last} {incr line} { set output 1 puts -nonewline "\n[string trimright [lindex $screen $line]]" } } ##puts "output<$output>" return $output } # Set 'verbose' to 1 to get debug output from the glue functions. set verbose 1 set debug 0 proc note {m} { global P puts "$P: $m" } proc warn {m} { global P puts "$P: WARNING: $m" } proc winge {m} { global P puts "$P: MACHINE ERROR: reboot failed - $m" } note "Logging into the hosting VM system to restart the VM guest" # Start s3270 Start # Setverbose 1 # Connect to the host and wait for an input field. Connect $hostname Wait InputField # Log in and wait for CP READ or VM READ mode. note "logging in ... sending username" String "$username\n" # Wait for the input field, to proceed further. If timed out screen scrape # the s3270 (error) message. if {[catch {set ret_val [Wait 10 InputField]} err]} { print_cons_buf note "password never prompted" exit 2 } # Sometimes the input field is emitted before the output from the VMserver. # Hence, after reading the input field wait for the output. if {[catch {set ret_val [Wait 10 Output]} err]} { print_cons_buf note "No output seen even after the reading the InputFiled" exit 2 } if { [Ascii 2 0 19 ] == "Enter your password" } { print_cons_buf note "pasword prompted ..." note "sending password ..." String "$password\n" } elseif { [Ascii 1 11 7] == "Already"} { # If we can't log on, we're hosed. Enter print_cons_buf warn "Already logged in. logging in here....." String "logon $username here\n" # Wait for the input field to proceed further if {[catch {set ret_val [Wait 10 InputField]} err]} { print_cons_buf note "password never prompted" exit 2 } if { [Ascii 2 0 19 ] == "Enter your password"} { print_cons_buf note "sending password ..." String "$password\n" } } elseif { [Ascii 1 20 19] == "not in CP directory" } { print_cons_buf note "'$username' is not a valid username" exit 1 } else { # Other unknown errors (firewall??) print_cons_buf warn "Unknown Error: Check the console log" exit 1 } if { [Ascii 1 11 23] == "PASSWORD NOT AUTHORIZED" } { while {1} { note "authorisation failure: PASSWORD NOT AUTHORIZED: holding ..." after 5000 } exit 1 } elseif { [Ascii 1 11 16] == "PASSWORD EXPIRED" } { while {1} { note "authorisation failure: PASSWORD EXPIRED: holding ..." after 5000 } note "Password expired" exit 1 } { note "Password authorized" } print_cons_buf waitread note "Connected to VM" # If we're in CP mode, which means we disconnected last time, boot CMS. if {[Ascii [expr [Rows]-1] [expr [Cols]-20] 2] == "Cp"} { note "Starting CMS" Clear String "i cms\n" String "no" waitread } Snap save set n [expr [Snap Rows]-2] set first 1 while {$n >= $first} { set lptext [Snap Ascii $first 0 [Snap Cols]] set lplength [string length [string trimright $lptext]] puts "$lptext " incr first } # Enter an empty command to get a CMS prompt. If we don't do this, there will # be a Ready prompt as the first line of output below. Enter cmd_done note "Executing command $command" cms_cmd $command Clear String "#cp term more 50 10\n" set timeouts {1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 5} set otimeout 0 set double 1 set cmdline {} while {1} { set ctimeout $otimeout interact { "" { puts -nonewline "\b \b" set cmdline [string range $cmdline 0 end-1] flush stdout } "" { puts -nonewline "\b \b" set cmdline [string range $cmdline 0 end-1] flush stdout } "\r" { ## When enter(alone) is pressesed, s3270 goes from ## "Running" to "Vm Read" mode. So, for actual one ## enter key press, two 'Enter'[a s3270 function] is ## required String $cmdline set cmdline {} Enter if {$double == 1} { Enter } set double 1 print_cons_buf if {$ctimeout != 0} { set otimeout 0 return } } -re "(.)" { set char $interact_out(0,string) puts -nonewline "$char" set cmdline "$cmdline$char" flush stdout set double 0 if {$ctimeout != 0} { set otimeout 0 return } } timeout [lindex $timeouts $otimeout] { ## Read the console buffer and print it ##puts "<>" if {[print_cons_buf]} { set otimeout 0 } elseif {$otimeout < 15} { incr otimeout } #puts "TIMEOUT: $otimeout [lindex $timeouts $otimeout]" return } } } conmux-0.12.0/drivers/reboot-lantronix0000755000175000017500000000215011376624514016520 0ustar loollool#!/usr/bin/expect # # Reboot a machine connected to a Lantronix SLC8 # # Copyright 2008 Google Inc., Scott Zawalski # Released under the GPL v2 set P "reboot-lantronix" if {[llength $argv] < 3} { puts stderr "Usage: $P " exit 1 } #Max number of attempts before stopping set max_attempts {5} set tshost [lindex $argv 0] set tsport [lindex $argv 1] set outlet [lindex $argv 2] set connected {0} set attempts {0} while {$connected == 0 && $attempts < $max_attempts} { spawn telnet $tshost $tsport sleep 5 send "\r" set timeout 15 set attempts [expr $attempts +1 ] expect { #Connection closed "Connection closed by foreign host." { puts "Retrying attempt $attempts" } #Already logged in "RPC-22>" { send "Reboot $outlet\r" expect "Reboot Outlet $outlet" send "Y\r" expect "Rebooting" expect "RPC-22" send "quit\r" puts "Machine successfully rebooted." exit 0 } timeout { puts "Timed out connecting." } } } puts "Unable to connect after $attempts connection attempts." exit 1 conmux-0.12.0/drivers/reboot-rsa0000755000175000017500000000353211376624514015274 0ustar loollool#!/usr/bin/expect # # reboot-rsa -- reboot systems with RSA management cards # # Reboot systems via their RSA (I) managment card interface. # # usage: # reboot-rsa ... # # example # reboot-rsa FOO BAR telnet 1.2.3.4 # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Dave Hansen # # The Console Multiplexor is released under the GNU Public License V2 # set P "reboot-rsa" if {[llength $argv] < 3} { puts stderr "Usage: $P ..." exit 1 } set username [lindex $argv 0] set password [lindex $argv 1] log_user 0 #stty echo #log_file -a "$logfile" proc note {m} { global P puts "$P: $m" } proc warn {m} { global P puts "$P: WARNING: $m" } proc winge {m} { global P puts "$P: ERROR: $m" } set elapsed_time 0 set timeout 10 set command [lrange $argv 2 end] eval spawn [lrange $argv 2 end] note "Logging into service processor with command \"$command\" to restart it"; expect { "Connection closed by foreign host." { winge "Telnet connection closed." exit 1; } "Unable to connect to remote host:" { winge "Someone may already have the service processor"; exit 2; } "Login ID:" { send "$username\t$password\r" } timeout { winge "never saw opening screen with \"Login ID:\"" exit 2; } } expect { "Log Off" { send "\t\t\t\t\r" note "Saw opening screen, selecting \"Server Power/Restart\""; } timeout { } } expect { "Restart Server Immediately" { note "Saw \"Server Power/Restart\" screen. Selecting \"Restart\""; send "\t\t\t\t\t\r"; } } expect { "Restarting the system immediately" { note "Saw restart confirmation prompt, pressing enter"; send "\r" } } expect { "The system is performing a Restart Server Immediately" { note "the system is restarting"; exit 0; } } winge "an error occurred while restarting the server" exit 1; conmux-0.12.0/drivers/reboot-cyclades0000755000175000017500000000272611376624514016302 0ustar loollool#!/usr/bin/expect # # Reboot a machine connected to a Cyclades PM IPDU # # Copyright 2008 Google Inc., Scott Zawalski # Based heavily off of reboot-sentry by Ryan Kubiak # Released under the GPL v2 set P "reboot-cyclades" if {[llength $argv] < 5} { puts stderr "Usage: $P " exit 1 } #Max number of attempts before stopping set max_attempts {5} set tshost [lindex $argv 0] set tsport [lindex $argv 1] set user [lindex $argv 2] set pass [lindex $argv 3] set outlet [lindex $argv 4] set connected {0} set attempts {0} while {$connected == 0 && $attempts < $max_attempts} { spawn telnet $tshost $tsport sleep 5 send "\r" set timeout 10 set attempts [expr $attempts +1 ] expect { #Connection closed "Connection closed by foreign host." { puts "Retrying attempt $attempts" } #Already logged in "pm>" { send "cycle $outlet\r" expect "Outlet turned off" expect "Outlet turned on" send "exit\r" puts "Machine successfully rebooted." exit 0 } #Login "Username" { send "$user\r" expect "Password:" send "$pass\r" expect "pm>" send "cycle $outlet\r" expect "Outlet turned off" expect "Outlet turned on" send "exit\r" puts "Machine successfully rebooted." exit 0 } timeout { puts "Timed out connecting." } } } puts "Unable to connect after $attempts connection attempts." exit 1 conmux-0.12.0/drivers/reboot-netfinity0000755000175000017500000000443111376624514016517 0ustar loollool#!/usr/bin/expect -f # # reboot-netfinity -- reboot netfinity machines via their management port # # This expect script reboots a machine via a managemant port. # It only works with the Netfinity-style Service Processors. # They have a number-driven interface # # example machines: Netfinity 6000R, 8500R # xSeries x350, x370 # # usage: # reboot-netfinity ... # # examples: # reboot-netfinity USERID PASSW0RD telnet 1.2.3.4 7033 # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Dave Hansen # # The Console Multiplexor is released under the GNU Public License V2 # set P "reboot-netfinity" if {[llength $argv] < 2} { puts stderr "Usage: $P ..." exit 1 } set argc [llength $argv] set userid [lindex $argv 0] set password [lindex $argv 1] set timeout 30 set send_slow {1 .1} set command [lrange $argv 2 end] puts "$P: Logging into service processor with command \"$command\" to restart it"; eval spawn [lrange $argv 2 end] send -s -- "\033\033\033\033\033" expect *; send "\033" expect { #Log in "USER ID:" { send -s -- "$userid\r" expect -exact "PASSWORD:" send -s -- "$password\r" exp_continue; } #Some machines use different login prompts "User_id:" { send -s -- "$userid\r" expect -exact "Password:" send -s -- "$password\r" exp_continue; } #already logged in "Z - Start Remote Video" { send "6" expect -exact "4 - Power On" send "3" expect -exact "2 - Power Off Immediately" send "2" expect -exact "0 - Write" send "0" expect -exact "6.3.2: Write" send -s -- "\033\033\033\033\033" expect -exact "Z - Start Remote Video" send "6" expect -exact "4 - Power On" send "4" expect -exact "2 - Power On Release CPU's Reset" send "2" expect -exact "0 - Write" send "0" expect -exact "6.4.2: Write" # On kernels with ACPI support, the above may send an ACPI # event to the OS to perform a clean shutdown. If that happens # we may not ever come back up. Add the reboot command below # to finish the job. send -s -- "\033\033\033\033\033" expect -exact "Z - Start Remote Video" send "7" expect -exact "4 - Restart SP" send "2" expect -exact "0 - Write" send "0" expect -exact "7.2: Write" } } puts "$P: reboot initiated" conmux-0.12.0/drivers/hmc0000755000175000017500000003163411376624514013772 0ustar loollool#!/usr/bin/expect -- # # hmc -- handle consoles and rebooting of HMC based systems # # Allow connecting to the console of and rebooting of HMC connected # machines. Machines are identified by the HMC IP address, connected # system name, and partition name. # # usage: # hmc open-term|reboot -h -m -p \ # -U -P -V # # example: # hmc open-term -m elm3b70 -p FullSystemPartition -U hscroot -P passwd \ # -V 2.6 -h 1.2.3.4 # hmc reboot -m elm3b70 -p FullSystemPartition -U hscroot -P passwd \ # -V 2.6 -h 1.2.3.4 # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # set P "hmc" # See interactions. log_user 0 #exp_internal 1 proc note {m} { global P puts "$P: $m" } proc warn {m} { global P puts "$P: WARNING: $m" } proc winge {m} { global P puts "$P: MACHINE ERROR: $m" } # # OPTIONS: options parser. # proc shift {_list} { upvar $_list list set res [lindex $list 0] set list [lreplace $list 0 0] return $res } proc arg {_list arg} { upvar $_list list if {[llength $list] < 1} { winge "$arg: required argument missing" exit 1 } return [shift list] } set user {hscroot} set host {} set system {} set lpar {} set profile {default} set mode {norm} set passwd {} set version {} set cmd [lindex $argv 0] shift argv while {[llength $argv] > 0} { switch -- [shift argv] { -h { set host [arg argv h]} hmc { set host [arg argv h]} -m { set system [arg argv m]} -l { set lpar [arg argv l]} -p { set lpar [arg argv p]} -f { set profile [arg argv f]} -b { set mode [arg argv b]} -U { set user [arg argv P]} -P { set passwd [arg argv P]} -V { set version [arg argv P]} } } if {[llength $argv] > 0} { puts stderr "Usage: $P -h -m -p -f -b " exit 1 } if {[string compare $host ""] == 0 || [string compare $system ""] == 0 || [string compare $lpar ""] == 0} \ { winge "hmc (-h), machine (-m) and lpar (-p) required" exit 1 } set prompt {___EOF___HMC___EOF___} set hscpath {/opt/hsc/bin} #log_file -a "$logfile" set elapsed_time 0 set timeout 30 # Ensure we have a terminal so we don't get prompted for one. if {![info exists env(TERM)]} { set env(TERM) {vt100} } # If we have a password don't use any ssh-keys we may have been offered. if {[string compare $passwd ""] == 0} { set command "ssh $env(SSH_OPTIONS) $user@$host" } else { set command "ssh $user@$host" } # CONNECT: connect to the remote console. Set the prompt up so we # know when a command we execute has completed. note "Logging into HMC console with command \"$command\" ..." eval spawn $command send "PS1=$prompt\r" expect { -re {[pP]assword:} { if {[string compare $passwd ""] == 0} { winge "$host: requesting password - not hacked" exit 2 } send "$passwd\r" after 500 send "PS1=$prompt\r" exp_continue } "Connection timed out" { winge "$host: cannot connect to console" exit 2 } "Name or service not known" { winge "$host: HMC name invalid" exit 1 } "Permission denied" { winge "$host: authentication failure, permission denied" exit 2 } timeout { winge "$host: HMC did not prompt us, timed out" exit 2 } eof { winge "$host: HMC disconnected without prompting" exit 2 } "Terminal type?" { send "vt100\r" send "PS1=$prompt\r" exp_continue } "PS1=$prompt" { # Ignore us typing the change to the prompt. exp_continue } "$prompt" { # Prompt see, we are in and working, drop out. ## note "prompt seen" } -re "^\[^\n]*\n" { # We need to absorb any login information, motd etc. ## puts "NOISE<$expect_out(buffer)>" exp_continue } } proc runit {cmd _out} { upvar $_out out global prompt send "$cmd" set text "" set line 0 set timeout 120 expect { -re "^\[^\n]*\n" { ##note "LINE: $expect_out(buffer)" if { $line > 0 } { append text $expect_out(buffer) } incr line exp_continue } -re "$prompt" { ##note "prompt seen" } timeout { set out "hmc: command timeout" return 255 } } set out [string trimright $text] send "echo \$?\r" set text "" set line 0 set timeout 30 expect { -re "^\[^\n]*\n" { ##note "LINE: $expect_out(buffer)" if { $line > 0 } { append text $expect_out(buffer) } incr line exp_continue } -re "$prompt" { ##note "prompt seen" } timeout { set out "hmc: command status timeout" return 255 } } ##note "EXIT: $text" return [string trimright $text] } proc extract_names {out} { set res {} foreach sys [split $out] { if {[string compare $sys ""] != 0} { lappend res $sys } } return $res } # Find out the current version of the HMC software. if {[string compare $version ""] == 0} { runit "rpm -q IBMhsc.coreserver --qf '%{VERSION}'; echo ''\r" version } if {[string compare $version ""] == 0} { winge "unable to obtain HMC code version" exit 1 } note "HMC revision v$version" # Based on the version of the HMC software select payload. if {$version < 4.0} { # # VERSION 2.6/3.0 specific support. # proc system_list {} { global hscpath # Ask for a list of systems. if {[runit "lssyscfg -r sys --all -F name\r" out] != 0} { winge "unable to obtain list of systems" exit 1 } return [extract_names $out] } proc system_state {system} { global hscpath # Ask for the system status. if {[runit "lssyscfg -r sys -n $system -F state\r" out] != 0} { winge "$system: failed to get status" exit 1 } return $out } proc lpar_list {system} { global hscpath # Ask for a list of lpars for this system. if {[runit "lssyscfg -r lpar -m $system --all -F name\r" out] != 0} { winge "unable to obtain list of lpars" exit 1 } return [extract_names $out] } proc state {system lpar} { global hscpath # Ask for the lpar status. if {[runit "lssyscfg -r lpar -m $system -n $lpar -F state\r" out] != 0} { winge "$system/$lpar: failed to get lpar status" exit 1 } return $out } proc reboot_metal {system lpar profile mode} { global hscpath # If we have no connection wait for the machine to appear. # XXX: timeout? set was {} set when [clock seconds] if {[string compare [system_state $system] "Ready"] != 0} { while {([clock seconds] - $when) < 60} { sleep 15 set state [system_state $system] note "waiting for stable connected state ($state)" if {$state != $was || [string compare $state "No Connection"] == 0} { set was $state set when [clock seconds] } } } # See if the system is is in Error, if so attempt to # power it on. if {[string compare [system_state $system] "Error"] == 0} { note "starting full system (from Error)" if {[runit "chsysstate -r sys -m $system -n $system -b $mode -o on -c full\r" out] == 0} { note "started" return } note "start failed - attempting shutdown" } # See if the system is up, if so shut it down. if {[string compare [system_state $system] "No Power"] != 0} { note "shutting down full system" if {[runit "chsysstate -r sys -m $system -n $system -o off -c full\r" out] != 0} { winge "$system: power off failed\n$out" exit 2 } } while {[string compare [system_state $system] "No Power"] != 0} { note "waiting for shutdown" sleep 15 } note "starting full system" if {[runit "chsysstate -r sys -m $system -n $system -b $mode -o on -c full\r" out] != 0} { winge "$system: power on failed" exit 2 } note "started" } proc reboot {system lpar profile mode} { global hscpath # Handle the bare metal separatly. if {[string compare $lpar "FullSystemPartition"] == 0} { return [reboot_metal $system $lpar $profile $mode] } # Partitions in Error state are tricky, you _may_ either # have to shut them down and then start them, or you may # just have to start them. So, if we are in Error start # the partition, if it fails we can just drop through # to the regular stop/start cycle. if {[string compare [state $system $lpar] "Error"] == 0} { note "starting lpar (from Error)" if {[runit "chsysstate -r lpar -m $system -n $lpar -b $mode -o on\r" out] == 0} { note "started" return } note "start failed - attempting shutdown" } # See if the lpar is up, if so shut it down. if {[string compare [state $system $lpar] "Ready"] != 0} { note "shutting down lpar" if {[runit "chsysstate -r lpar -m $system -n $lpar -o off\r" out] != 0} { winge "$system/$lpar: power off failed\n$out" exit 2 } } while {[string compare [state $system $lpar] "Ready"] != 0} { note "waiting for shutdown" sleep 15 } note "starting lpar" if {[runit "chsysstate -r lpar -m $system -n $lpar -b $mode -o on\r" out] != 0} { winge "$system/$lpar: power on failed\n$out" exit 2 } note "started" } } if {$version >= 4.0} { # # VERSION 4.x specific support. # proc system_list {} { global hscpath # Ask for a list of systems. if {[runit "lssyscfg -r sys -F name\r" out] != 0} { winge "unable to obtain list of systems" exit 1 } return [extract_names $out] } proc system_state {system} { global hscpath # Ask for the system status. if {[runit "lssyscfg -r sys -m $system -F state\r" out] != 0} { winge "$system: failed to get system status" exit 1 } return $out } proc lpar_list {system} { global hscpath # Ask for a list of lpars for this system. if {[runit "lssyscfg -r lpar -m $system -F name\r" out] != 0} { winge "unable to obtain list of lpars" exit 1 } return [extract_names $out] } proc state {system lpar} { global hscpath # Ask for the lpar status. if {[runit "lssyscfg -r lpar -m $system --filter lpar_names=$lpar -F state\r" out] != 0} { winge "$system/$lpar: failed to get lpar status" exit 1 } return $out } proc reboot {system lpar profile mode} { global hscpath # Partitions in Error state are tricky, you _may_ either # have to shut them down and then start them, or you may # just have to start them. So, if we are in Error start # the partition, if it fails we can just drop through # to the regular stop/start cycle. if {[string compare [state $system $lpar] "Error"] == 0} { note "starting lpar (from Error)" if {[runit "chsysstate -r lpar -m $system -n $lpar -f $profile -b $mode -o on\r" out] == 0} { note "started" return } note "start failed - attempting shutdown" } # See if the lpar is up, if so shut it down. if {[string compare [state $system $lpar] "Not Activated"] != 0} { note "shutting down lpar" if {[runit "chsysstate -r lpar -m $system -n $lpar -o shutdown --immed\r" out] != 0} { winge "$system/$lpar: power off failed\n$out" exit 2 } } while {[string compare [state $system $lpar] "Not Activated"] != 0} { note "waiting for shutdown" sleep 15 } note "starting lpar" if {[runit "chsysstate -r lpar -m $system -n $lpar -f $profile -b $mode -o on\r" out] != 0} { winge "$system/$lpar: power on failed\n$out" exit 2 } note "started" } } # # VERSION: common # proc open-term {system lpar} { global prompt global hscpath global spawn_id note "opening console ..." send "ulimit -t 3600; \[ -f cleandown ] && sudo /home/hscroot/cleandown $system $lpar; mkvterm -m $system -p $lpar\r" expect { "NVTS" { } "Open in progress.." { } "$prompt" { winge "$system/$lpar: open console failed" exit 2 } } note "console open" interact { -i $spawn_id " Connection has closed" { winge "$system/$lpar: console connection closed" exit 2 } {Error in communication path to the partition} { winge "$system/$lpar: lost contact with lpar" exit 2 } "$prompt" { winge "$system/$lpar: open console failed" exit 2 } eof { winge "$system/$lpar: payload lost" exit 2 } \000 { } } note "console closed" } proc close-term {system lpar} { global hscpath note "closing console ..." if {[runit "rmvterm -m $system -p $lpar\r" out] != 0} { winge "$system/$lpar: close console failed" exit 2 } } # Look and see if this system exists. set systems [system_list] if {[lsearch -exact $systems $system] < 0} { winge "$system: system not known; console knows: $systems" exit 1 } set lpars [lpar_list $system] if {[lsearch -exact $lpars $lpar] < 0} { winge "$system/$lpar: lpar not known; console knows: $lpars" exit 1 } # Check for system in Operating/Ready. set state [system_state $system] if {[string compare $lpar "FullSystemPartition"] != 0 && [string compare $state "Operating"] != 0 && [string compare $state "Ready"] != 0} \ { winge "$system: unusable - $state" exit 1 } # Ask for the lpar status, to see if it exists. set lstate [state $system $lpar] note "$system/$lpar: found ($state/$lstate)" # # COMMANDS: command. # switch -- $cmd { {open-term} { close-term $system $lpar open-term $system $lpar } {close-term} { close-term $system $lpar } {reboot} { reboot $system $lpar $profile $mode } default { winge "$cmd: unknown command" } } exit 0 conmux-0.12.0/drivers/reboot-rsa20000755000175000017500000000306511376624514015357 0ustar loollool#!/usr/bin/expect # # reboot-rsa2 -- reboot systems via their RSA-II management cards # # Use a systems RSA (II) managment card to hard reset that system. # # usage: # reboot-rsa2 ... # # examples: # reboot-rsa2 FOO BAR telnet 1.2.3.4 23 # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Aileen Sheedy # # The Console Multiplexor is released under the GNU Public License V2 # set P "reboot-rsa2" if {[llength $argv] < 3} { puts stderr "Usage: $P ..." exit 1 } set username [lindex $argv 0] set password [lindex $argv 1] log_user 0 #stty echo #log_file -a "$logfile" proc note {m} { global P puts "$P: $m" } proc warn {m} { global P puts "$P: WARNING: $m" } proc winge {m} { global P puts "$P: ERROR: $m" } set elapsed_time 0 set timeout 10 set command [lrange $argv 2 end] eval spawn [lrange $argv 2 end] note "Logging into service processor with command \"$command\" to restart it" expect { "Connection closed by foreign host." { winge "Telnet connection closed." exit 1 } "Unable to connect to remote host:" { winge "Someone may already have the service processor" exit 2 } "username:" { send "$username\r$password\r" } timeout { winge "Never saw opening screen with \"username:\"" exit 2 } } expect { "Invalid login!" { winge "Invalid username or password" exit 2 } ">" { send "reset\r" } } expect { "ok" { note "System restarting" send "exit\r" exit 0 } timeout { } } winge "an error occurred while restarting the server" exit 1 conmux-0.12.0/drivers/reboot-sentry0000755000175000017500000000322111376624514016026 0ustar loollool#!/usr/bin/expect # # Reboot a machine connected to a Servertech Sentry power strip # # Copyright 2008 Google Inc., Ryan Kubiak # Released under the GPL v2 set P "reboot-sentry" if {[llength $argv] < 3} { puts stderr "Usage: $P " exit 1 } set user {admn} set pass {admn} set tshost [lindex $argv 0] set tsport [lindex $argv 1] set outlet [lindex $argv 2] set connected {0} set attempts {0} while {$connected == 0 && $attempts < 10} { spawn telnet $tshost $tsport sleep 5 send "\r" set timeout 10 set attempts [ expr $attempts +1 ] expect { #Connection closed "Connection closed by foreign host." { puts "Retrying attempt $attempts" set rand [expr round(rand() * 15 + 6)] puts "Waiting $rand before next attempt" sleep $rand } #Already logged in "Switched CDU:" { set connected {1} send "reboot $outlet\r" expect "Command successful" send "logout\r" puts "Machine successfully rebooted." exit 0 } #Login "Username" { set connected {1} send "$user\r" expect "Password:" send "$pass\r" expect "Switched CD:" send "reboot $outlet\r" expect "Command successful" send "logout\r" puts "Machine successfully rebooted." exit 0 } timeout { puts "Timed out connecting." } } } puts "Unable to connect after 10 connection attempts." exit 1 conmux-0.12.0/drivers/reboot-newisys0000755000175000017500000000231111376624514016202 0ustar loollool#!/usr/bin/expect -f # # reboot-newisys -- reboot a newisys system via its management interface # # This expect script reboots a newisys machine using its command # interface. This is typically accessed over ssh. # # usage: # reboot-newisys ... # # examples: # reboot-newisys USERID PASSW0RD ssh 1.2.3.4 # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Daniel Sauble # # The Console Multiplexor is released under the GNU Public License V2 # set P "reboot-newisys" if {[llength $argv] != 4} { puts stderr "Usage: $P " exit 1 } set userid [lindex $argv 0] set password [lindex $argv 1] set machine [lindex $argv 3] set timeout 30 puts "$P: connecting to $machine to initiate reboot ..." # Log in eval spawn "ssh -o StrictHostKeyChecking=no $userid\@$machine" expect "password: " send -- "$password\r" expect "localhost \$ " send "platform get power state\r" # Check to see if the machine is on or off # then restart it expect { "On" {send "platform set power state cycle -f\r"} "Off" {send "platform set power state on -f\r"} } expect "localhost \$ " expect "localhost \$ " send "exit\r" puts "$P: reboot initated" conmux-0.12.0/drivers/reboot-acs480000755000175000017500000000141011376624514015422 0ustar loollool#! /usr/bin/expect -- # # Reboot a machine connected to an ACS48 terminal server which is appropriately # configured to use power management equipment. Assumes that ^P is power menu # hotkey (this is the default). # # Copyright 2006 Google Inc. # Author: sqazi@google.com (Salman Qazi) # set machine [lindex $argv 0] set ts_addr [lindex $argv 1] spawn ssh root:$machine@$ts_addr sleep 5 set timeout 10 expect { "Enter your option :" { send "4\r" expect { "Enter session PID or \'all\':" { send "all\r" } } } } expect { "All sessions killed." { send \020 expect { "Please choose an option:" { send "5\r" expect { "Outlet turned on." {send "\r~.\r"} } } } } } conmux-0.12.0/drivers/x3270_glue.expect0000644000175000017500000001701511376624514016303 0ustar loollool# Copyright 2000, 2004 by Paul Mattes. # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, # provided that the above copyright notice appear in all copies and that # both that copyright notice and this permission notice appear in # supporting documentation. # # x3270, c3270, s3270 and tcl3270 are distributed in the hope that they will # be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE # for more details. # Glue functions between 'expect' and x3270 # Usage: source x3270_glue.expect namespace eval x3270 { variable verbose 0 variable pid 0 # Start function: Start ?-nohup? ?program? ?options? # # Sets up the 'expect' environment correctly and spawns a 3270 # interface process. # # The 'program' and 'options' can be: # "x3270 -script" to drive an x3270 session # "s3270" to drive a displayless 3270 session # "x3270if -i" to run as a child script of x3270 (via the Script() # action) # # If "args" is empty, or starts with an option besides '-nohup', # guesses which process to start. # It will only guess "x3270if -i" or "s3270"; if you want to start # x3270, you need to specify it explicitly. # # Returns the process ID of the spawned process. proc Start {args} { global stty_init timeout spawn_id env variable verbose variable pid if {$pid != 0} {return -code error "Already started."} # If the first argument is "-nohup", remember that as an # argument to 'spawn'. if {[lindex $args 0] == "-nohup"} { set nohup {-ignore HUP} set args [lrange $args 1 end] } { set nohup {} } # If there are no arguments, or the first argument is an # option, guess what to start. # If X3270INPUT is defined in the environment, this must be a # child script; start x3270if. Otherwise, this must be a peer # script; start s3270. if {$args == {} || [string index [lindex $args 0] 0] == "-"} { if {[info exists env(X3270INPUT)]} { set args [concat x3270if -i $args] } { set args [concat s3270 $args] } } # Set up the pty initialization default. set stty_init -echo # Spawn the process. if {$verbose} { set pid [eval [concat spawn $nohup $args]] } { set pid [eval [concat spawn -noecho $nohup $args]] log_user 0 } # Set the 'expect' timeout. set timeout -1 return $pid } # Basic interface command. Used internally by the action functions # below. proc cmd {cmd} { variable verbose variable pid if {$pid==0} { return -code error "Not started yet." } if {$verbose} {puts "+$cmd"} send "$cmd\r" expect { -re "data: (.*)\r\n.*\r\nok\r\n$" { set r $expect_out(buffer) } "*ok\r\n" { return {} } -re "(.*)\r\n.*?\r\nerror\r\n" { return -code error "$expect_out(1,string)" } "*error\r\n" { return -code error \ "$cmd failed: $expect_out(buffer)" } eof { set pid 0; error "process died" } } # Convert result to a list. set ret {} set iter 0 while {1} { if {! [regexp "data: (.*?)\r\n" $r dummy elt]} {break} if {$iter==1} {set ret [list $ret]} set r [string range $r [expr [string length $elt]+7] \ end] if {$iter > 0} { set ret [linsert $ret end $elt] } { set ret $elt } set iter [expr $iter + 1] } if {$verbose} {puts "ret $iter"} return $ret } # Convert an argument list to a comma-separated list that x3270 will # accept. proc commafy {alist} { set i 0 set a "" while {$i < [llength $alist]} { if {$i > 0} { set a "$a,[lindex $alist $i]" } { set a [lindex $alist $i] } incr i } return $a } # Quote a text string into x3270-acceptable format. proc stringify {text} { set a "\"" set i 0 while {$i < [string len $text]} { set c [string range $text $i $i] switch -- $c { "\n" { set a "$a\\n" } "\r" { set a "$a\\r" } " " { set a "$a\\ " } "\"" { set a "$a\\\"" } default { set a "$a$c" } } incr i } set a "$a\"" return $a } # User-accessible actions. # Some of these apply only to x3270 and x3270if, and not to s3270. proc AltCursor {} { return [cmd "AltCursor"] } proc Ascii {args} { return [cmd "Ascii([commafy $args])"] } proc AsciiField {} { return [cmd "AsciiField"] } proc Attn {} { return [cmd "Attn"] } proc BackSpace {} { return [cmd "BackSpace"] } proc BackTab {} { return [cmd "BackTab"] } proc CircumNot {} { return [cmd "CircumNot"] } proc Clear {} { return [cmd "Clear"] } proc CloseScript {} { return [cmd "CloseScript"] } proc Cols {} { return [lindex [Status] 7] } proc Compose {} { return [cmd "Compose"] } proc Connect {host} { return [cmd "Connect($host)"] } proc CursorSelect {} { return [cmd "CursorSelect"] } proc Delete {} { return [cmd "Delete"] } proc DeleteField {} { return [cmd "DeleteField"] } proc DeleteWord {} { return [cmd "DeleteWord"] } proc Disconnect {} { return [cmd "Disconnect"] } proc Down {} { return [cmd "Down"] } proc Dup {} { return [cmd "Dup"] } proc Ebcdic {args} { return [cmd "Ebcdic([commafy $args])"] } proc EbcdicField {} { return [cmd "EbcdicField"] } proc Enter {} { return [cmd "Enter"] } proc Erase {} { return [cmd "Erase"] } proc EraseEOF {} { return [cmd "EraseEOF"] } proc EraseInput {} { return [cmd "EraseInput"] } proc FieldEnd {} { return [cmd "FieldEnd"] } proc FieldMark {} { return [cmd "FieldMark"] } proc FieldExit {} { return [cmd "FieldExit"] } proc Flip {} { return [cmd "Flip"] } proc HexString {x} { return [cmd "HexString($x)"] } proc Home {} { return [cmd "Home"] } proc Info {text} { return [cmd "Info([stringify $text])"] } proc Insert {} { return [cmd "Insert"] } proc Interrupt {} { return [cmd "Interrupt"] } proc Key {k} { return [cmd "Key($k)"] } proc Keymap {k} { return [cmd "Keymap($k)"] } proc Left {} { return [cmd "Left"] } proc Left2 {} { return [cmd "Left2"] } proc MonoCase {} { return [cmd "MonoCase"] } proc MoveCursor {r c} { return [cmd "MoveCursor($r,$c)"] } proc Newline {} { return [cmd "Newline"] } proc NextWord {} { return [cmd "NextWord"] } proc PA {n} { return [cmd "PA($n)"] } proc PF {n} { return [cmd "PF($n)"] } proc PreviousWord {} { return [cmd "PreviousWord"] } proc Quit {} { exit } proc Reset {} { return [cmd "Reset"] } proc Right {} { return [cmd "Right"] } proc Right2 {} { return [cmd "Right2"] } proc Rows {} { return [lindex [Status] 6] } proc SetFont {font} { return [cmd "SetFont($font)"] } proc Snap {args} { return [cmd "Snap([commafy $args])"] } proc Status {} { variable verbose variable pid if {$pid==0} { return -code error "Not started yet." } if {$verbose} {puts "+(nothing)"} send "\r" expect { "*ok\r\n" { set r $expect_out(buffer) } eof { set pid 0; error "process died" } } return [string range $r 0 [expr [string length $r]-7]] } proc String {text} { return [cmd "String([stringify $text])"] } proc SysReq {} { return [cmd "SysReq"] } proc Tab {} { return [cmd "Tab"] } proc ToggleInsert {} { return [cmd "ToggleInsert"] } proc ToggleReverse {} { return [cmd "ToggleReverse"] } proc TemporaryKeymap {args} { return [cmd "TemporaryKeymap($args)"] } proc Transfer {args} { return [cmd "Transfer([commafy $args])"] } proc Up {} { return [cmd "Up"] } proc Wait {args} { return [cmd "Wait([commafy $args])"] } # Extra function to toggle verbosity on the fly. proc Setverbose {level} { variable verbose set verbose $level return } # Export all the user-visible functions. namespace export \[A-Z\]* } # Import all of the exported functions. namespace import x3270::* conmux-0.12.0/drivers/reboot-laurel0000755000175000017500000000627111376624514015776 0ustar loollool#!/usr/bin/expect ############################################################################### # reboot-laurel -- Reboot a system attached to a Laurel DPM meter. # # This script provides standalone control for the Laurel DPM ammeter when # setup to control power to a target using the alarm relays. This is actually # a creative misuse of the meter. We are actually sending commands to the # meter that trigger alarm relays to be "always on" or "always off". # # In addition to controlling power to a target, the meter will report the # current ammeter reading using the 'value' command. # # Usage: # reboot-laurel # # Copyright (c) 2007, Wind River Systems # Author: James Puderer # ############################################################################### # This script depends on the ckermit package. ##### Configuration ##### set tty_line /dev/ttyUSB0 set tty_baud 9600 set reset_delay 5000 ######################### log_user 0 # Triggers alarm relay when over a large negative amperage (always true) set command(relay1_on) "*1F386800000\r" set command(relay2_on) "*1F389800000\r" # Triggers alarm relay when under a large positive amperage (always false) set command(relay1_off) "*1F3867FFFFF\r" set command(relay2_off) "*1F3897FFFFF\r" set command(get_value) "*1B1\r" proc usage { } { global argv0 puts stderr "Usage: $argv0 \n" exit 1 } proc connect { tty_line tty_baud } { global spawn_id spawn kermit -Y expect { "C-Kermit>" { } timeout { error "Timeout starting kermit" } } send "set line $tty_line\n" send "set speed $tty_baud\n" expect { "$tty_baud bps" { } "Sorry, device is in use" { error "Serial device is in use" } timeout { error "Kermit timed out" } } send "set flow none\n" send "set carrier-watch off\n" send "c\n" expect { "Port already in use" { error "Port already in use" } "\n-------*\n" { } timeout { error "Kermit timed out" } } } proc disconnect { } { global spawn_id close } proc on { } { global spawn_id command send $command(relay1_on) after 100 send $command(relay2_on) after 100 } proc off { } { global spawn_id command send $command(relay1_off) after 100 send $command(relay2_off) after 100 } proc reset { } { global spawn_id reset_delay command send $command(relay1_off) after 100 send $command(relay2_off) after $reset_delay send $command(relay1_on) after 100 send $command(relay2_on) after 100 } proc value { } { global spawn_id command set timeout 1 expect "*" send $command(get_value) expect { -re {[-| ][0-9][0-9]\.[0-9][0-9][0-9]} { return $expect_out(0,string) } } } # Handle command line if {[llength $argv] < 2} { usage } set tty_line [lindex $argv 0] set action [string tolower [lindex $argv 1]] if { ($action == "on") || ($action == "off") || ($action == "reset") } { connect $tty_line $tty_baud $action disconnect } elseif { $action == "value" } { connect $tty_line $tty_baud puts [$action] disconnect } else { usage } conmux-0.12.0/conmux-attach0000755000175000017500000000346211376624514014316 0ustar loollool#!/usr/bin/perl # # conmux-attach -- attach commands to the console # # Attach stand alone commands to a console stream. You can specify # which of the commands file descriptors are connected to which of the # 'steams' in the multiplexor; the console input/output and the user # output. # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # use FindBin; use IO::Socket; use Getopt::Long qw(:config no_auto_abbrev); # Find our internal libraries. use lib $FindBin::Bin; use Conmux; my $P = 'conmux-attach'; my ($in, $out, $err); # Usage. GetOptions( 'i|stdout=s' => \$in, 'o|stdin=s' => \$out, 'e|stderr=s' => \$err, 'b|bot=s' => \$bot, ) or exit; if ($in eq '' && $out eq '' && $err eq '') { $in = 'client'; $out = 'client'; } if ($#ARGV < 1) { print STDERR "$P: ...\n"; exit 1; } my ($mux, $app) = @ARGV; shift; $app =~ s@.*/@@; $app =~ s@\s.*$@@; $app = $bot if ($bot); # # Connect to the client channel. # sub conmux_connect { my ($mux, $type) = @_; if (!$cache{$mux, $type}) { my $con = Conmux::connect($mux); my %r = Conmux::sendCmd($con, 'CONNECT', { 'id' => 'bot:' . $app, 'to' => 'console', 'type' => $type } ); die "$P: $mux: connect failed - $r{'status'}\n" if ($r{'status'} ne "OK"); $cache{$mux, $type} = $con; } $cache{$mux, $type}; } if ($in) { my $to = conmux_connect($mux, $in); open(STDIN, "<&", $to) || die "$P: unable to hand off socket to stdin - $!\n"; } if ($out) { my $to = conmux_connect($mux, $out); open(STDOUT, ">&", $to) || die "$P: unable to hand off socket to stdout - $!\n"; } if ($err) { my $to = conmux_connect($mux, $err); open(STDERR, ">&", $to) || die "$P: unable to hand off socket to stderr - $!\n"; } exec @ARGV; conmux-0.12.0/conmux-registry0000755000175000017500000001346411376624514014725 0ustar loollool#!/usr/bin/perl # # conmux-registry -- console name registry server # # Main registry server. This server holds host/port assignments for # conmux daemons registering with it. This allows users to specify # human names for their consoles and find the relevant conmux daemon. # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # use strict; use FindBin; use Symbol qw(gensym); use IO::Socket; use IO::Multiplex; use URI::Escape; # Find our internal libraries. use lib $FindBin::Bin; use Conmux; our $P = 'conmux-registry'; our $debug = 0; # # LISTENER SOCKET: creates an intenet listener for new clients and # connects them to the junction provided. # package ListenerSocket; sub new { my ($class, $mux, $port, $registry) = @_; my $self = bless { 'mux' => $mux, 'registry' => $registry }, $class; print "ListenerSocket::new [$self] mux<$mux> port<$port> " . "registry<$registry>\n" if ($main::debug); $self->initialise($mux, $port, $registry); $self; } sub initialise { my ($self, $mux, $port, $registry) = @_; my ($sock); print "ListenerSocket::initialise [$self] mux<$mux> port<$port> " . "registry<$registry>\n" if ($main::debug); # Create a listening socket and add it to the multiplexor. my $sock = new IO::Socket::INET(Proto => 'tcp', LocalPort => $port, Listen => 4, ReuseAddr => 1) or die "socket: $@"; print " adding $self $sock\n" if ($main::debug); $mux->listen($sock); $mux->set_callback_object($self, $sock); $self->{'listener'} = $sock; } # Handle new connections by instantiating a new client class. sub mux_connection { my ($self, $mux, $fh) = @_; my ($client); print "ListenerSocket::mux_connection [$self] mux<$mux> fh<$fh>\n" if ($main::debug); # Make a new client connection. $client = Client->new($mux, $fh, $self->{'registry'}); print " new connection $self $client\n" if ($main::debug); } sub DESTROY { my ($self) = @_; print "ListenerSocket::DESTROY [$self]\n" if ($main::debug); close($self->{'listener'}); } # # CLIENT: general client object, represents a remote client channel # package Client; sub new { my ($class, $mux, $fh, $registry) = @_; my $self = bless { 'mux' => $mux, 'fh' => $fh, 'registry' => $registry }, $class; print "Client::new [$self] mux<$mux> fh<$fh> registry<$registry>\n" if ($main::debug); $self->initialise($mux, $fh, $registry); $self; } sub initialise { my ($self, $mux, $fh, $registry) = @_; print "Client::initialise [$self] mux<$mux> fh<$fh> " . "registry<$registry>\n" if ($main::debug); $mux->set_callback_object($self, $fh); } sub mux_input { my ($self, $mux, $fh, $input) = @_; print "Client::mux_input [$self] mux<$mux> fh<$fh> input<$$input>\n" if ($main::debug); while ($$input =~ s/^(.*?)\n//) { my ($cmd, $args) = split(' ', $1, 2); my (%args) = Conmux::decodeArgs($args); my $reply = { 'status' => 'ENOSYS', }; # Fill in the common results. $reply->{'title'} = 'registry'; # Handle this command. if ($cmd eq "LOOKUP") { my $r = $self->{'registry'}->lookup($args{'service'}); if (defined $r) { $reply->{'result'} = $r; $reply->{'status'} = 'OK'; } else { $reply->{'status'} = 'ENOENT entry not found'; } } elsif ($cmd eq "ADD") { $self->{'registry'}->add($args{'service'}, $args{'location'}); $reply->{'status'} = 'OK'; } elsif ($cmd eq "LIST") { $reply->{'result'} = $self->{'registry'}->list(); $reply->{'status'} = 'OK'; } $fh->write(Conmux::encodeArgs($reply) . "\n"); } } sub mux_eof { my ($self, $mux, $fh, $input) = @_; print "Client::mux_eof [$self] mux<$mux> fh<$fh> input<$input>\n" if ($main::debug); # Handle any pending input, then remove myself. $self->mux_input($mux, $fh, $input); # Tell the multiplexor we no longer are using this channel. $mux->shutdown($fh, 1); } sub mux_close { my ($self, $mux, $fn) = @_; print "Client::close [$self]\n" if ($main::debug); } sub DESTROY { my ($self) = @_; print "Client::DESTROY [$self]\n" if ($main::debug); } # # REGISTRY: registry elements. # package Registry; sub new { my ($class, $store) = @_; my $self = bless { 'store' => $store }, $class; my ($key, $val); print "Registry::new [$self] store<$store>\n" if ($main::debug); # Open the store and populate the keys. open(S, '<', $store) || die "Registry::new: $store: open failed - $!\n"; while () { chomp; ($key, $val) = split(' ', $_); $self->{'key'}->{$key} = $val; } close(S); $self; } sub add { my ($self, $what, $where) = @_; my ($key); print "Registry::add [$self] what<$what> where<$where>\n" if ($main::debug); $self->{'key'}->{$what} = $where; print "$what at $where\n"; if (open(S, '>', $self->{'store'} . '.new')) { foreach $key (sort keys %{$self->{'key'}}) { print S "$key $self->{'key'}->{$key}\n"; } close(S); rename $self->{'store'} . '.new', $self->{'store'}; } else { warn "$P: $self->{'store'}.new: open failed - $!"; } } sub lookup { my ($self, $what) = @_; print "Registry::lookup [$self] what<$what>\n" if ($main::debug); $self->{'key'}->{$what}; } sub list { my ($self) = @_; my ($r, $key); print "Registry::list [$self]\n" if ($main::debug); foreach $key (sort keys %{$self->{'key'}}) { $r .= "$key $self->{'key'}->{$key}\n"; } $r; } # # MAIN: makes the IO multiplexor, listener and registry and stitches # them all together. # package main; # Usage checks. if ($#ARGV != 1) { print STDERR "Usage: $P \n"; exit 1 } my ($lport, $store) = @ARGV; # Make a new multiplexer. my $mux = new IO::Multiplex; # Make the registry object. my $registry = Registry->new($store); # Create the client listener socket. my $listener = ListenerSocket->new($mux, $lport, $registry); # Hand over to the multiplexor. $mux->loop; conmux-0.12.0/Makefile0000644000175000017500000000216511376624514013254 0ustar loollool# (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 BUILD= PREFIX=/usr/local/conmux BASE=$(BUILD)$(PREFIX) BINS=console conmux-attach LIBS=Conmux.pm SBIN=conmux-registry conmux start MODULES=helpers drivers all:: install:: @[ -d $(BASE) ] || mkdir -p $(BASE) @[ -d $(BASE)/bin ] || mkdir $(BASE)/bin @[ -d $(BASE)/lib ] || mkdir $(BASE)/lib @[ -d $(BASE)/sbin ] || mkdir $(BASE)/sbin @[ -d $(BASE)/log ] || mkdir $(BASE)/log @[ -d $(BASE)/etc ] || mkdir $(BASE)/etc for f in $(BINS); do \ rm -f $(BASE)/bin/$$f; \ cp -p $$f $(BASE)/bin/$$f; \ chmod 755 $(BASE)/bin/$$f; \ done for f in $(SBIN); do \ rm -f $(BASE)/sbin/$$f; \ cp -p $$f $(BASE)/sbin/$$f; \ chmod 755 $(BASE)/sbin/$$f; \ done for f in $(LIBS); do \ rm -f $(BASE)/lib/$$f; \ cp -p $$f $(BASE)/lib/$$f; \ chmod 644 $(BASE)/lib/$$f; \ done release:: $(MAKE) BUILD=build install (cd build; tar cf - *) | gzip >conmux.tgz rm -rf build clean:: rm -f conmux.tgz include $(patsubst %, %/module.mk, $(MODULES)) conmux-0.12.0/examples/0000755000175000017500000000000011376624514013426 5ustar loolloolconmux-0.12.0/examples/laurel.cf0000644000175000017500000000050511376624514015224 0ustar loolloollistener n800 application console 'n800 Console' 'cu -l /dev/ttyS0 -s 115200 dir' command 'hardreset' 'Reset target' 'reboot-laurel /dev/ttyUSB0 reset' command 'amps' 'Got amperage' 'reboot-laurel /dev/ttyUSB0 value' help 'hardreset' 'Resets target by cycling power' help 'amps' 'Displays current power consumption in amps' conmux-0.12.0/examples/command.cf0000644000175000017500000000047711376624514015366 0ustar loolloollistener ppc64 application console 'ppc64 console' 'hmc open-term -m bigppc -p bigppc-lpar1 -U user -P passwd -V 2.6 hmc 1.2.3.4' command 'hardreset' 'initated a hard reset' 'hmc reboot -m bigppc -p bigppc-lpar1 -U user -P passwd -V 2.6 hmc 1.2.3.4' help 'hardreset' 'initiate a system hard reset for this machine' conmux-0.12.0/examples/socket.cf0000644000175000017500000000034411376624514015231 0ustar loolloollistener hummer socket console 'hummer console' '1.2.3.4:2088' command 'hardreset' 'initated a hard reset' 'reboot-numaq vcs 1.2.3.5 hummer 12346 user passwd' help 'hardreset' 'initiate a system hard reset for this machine' conmux-0.12.0/examples/direct.cf0000644000175000017500000000013311376624514015207 0ustar loolloollistener steerpike application console 'steerpike console' 'cu -l /dev/ttyS0 -s 57600 dir' conmux-0.12.0/examples/README0000644000175000017500000000052211376624514014305 0ustar loolloolEXAMPLES ======== This directory contains some sample configuration files for conmux. socket.cf -- where the console is exposed directly on a tcp port on the local network. command.cf -- where the console is obtained through a driver. Details on the command line options for each driver are in the comments at the top of the driver. conmux-0.12.0/contrib/0000755000175000017500000000000011376624514013250 5ustar loolloolconmux-0.12.0/contrib/console_check.py0000755000175000017500000002457111376624514016435 0ustar loollool#!/usr/bin/python _author_ = 'Scott Zawalski (scottz@google.com)' """Console check script to be used with conmux. Checks if machines are not only connected to conmux but also responding in an expected way Supports options to show all, good, bad, unknown and add them to autotest as well. *In order for the power update option to work you have to have access to the etc directory of the conmux server """ import sys, pexpect, commands, os from optparse import OptionParser def main(argv): consoles = {} consoles['good'] = [] consoles['bad'] = [] consoles['unknown'] = [] # 0, 1, 2 status STATUS = [ 'good', 'bad', 'unknown'] parser = OptionParser() parser.add_option('--conmux-server', dest="conmux_server", default='localhost', help="Conmux server to connect to") parser.add_option('--conmux-dir', dest="conmux_dir", default='/usr/local/conmux', help="Conmux server to connect to") parser.add_option('--console-binary', dest="console_binary", default='/usr/local/conmux/bin/console', help="Conmux console binary location") parser.add_option('--autotest-cli-dir', dest="autotest_cli_dir", default='/usr/local/autotest/cli', help="Autotest CLI dir") parser.add_option('--add-hosts', action="store_true", dest="add_hosts", default=False, help="If host not on autotest server try to add it") parser.add_option('--power-label', dest="power_label", default='remote-power', help="Label to add to hosts that support hard reset") parser.add_option('--console-label', dest="console_label", default='console', help="Label to add to hosts that support console") parser.add_option('--update-console-label', action="store_true", dest="update_console_label", default=False, help="Update console label on autotest server") parser.add_option('--update-power-label', action="store_true", dest="update_power_label", default=False, help="Update power label on autotest server" +\ "*Note this runs then exists no consoles are checked") parser.add_option('--verbose', action="store_true", dest="verbose", default=False, help="Verbose output") parser.add_option('--show-bad', action="store_true", dest="show_bad", default=False, help="Show consoles that are no longer functioning") parser.add_option('--show-good', action="store_true", dest="show_good", default=False, help="Show consoles that are functioning properly") parser.add_option('--show-unknown', action="store_true", dest="show_unknown", default=False, help="Show consoles that are in an unknown state") parser.add_option('--show-all', action="store_true", dest="show_all", default=False, help="Show status of all consoles") options, args = parser.parse_args() if len(argv) == 2 and options.verbose: parser.print_help() return 1 elif len(argv) < 2: parser.print_help() return 1 if options.update_power_label: remove_create_label(options.power_label, options.autotest_cli_dir) update_power_label(options.power_label, options.conmux_dir, options.autotest_cli_dir, options.add_hosts) return print options.console_binary if not os.path.exists(options.console_binary): print "Error %s does not exist, please specify another path" %\ options.console_binary return 1 hosts = get_console_hosts(options.console_binary, options.conmux_server) for host in hosts: rc = check_host(host, options.console_binary) if options.verbose is True: print "%s status: %s" % (host, STATUS[rc]) consoles[STATUS[rc]].append(host) if options.show_all: for status in consoles: print "--- %s ---" % status for host in consoles[status]: print host if options.show_good: print "--- good ---" for host in consoles['good']: print host if options.show_bad: print "--- bad ---" for host in consoles['bad']: print host if options.show_unknown: print "--- unknown ---" for host in consoles['unknown']: print host if options.update_console_label: remove_create_label(options.console_label, options.autotest_cli_dir) update_console_label(options.console_label, consoles['good'], options.autotest_cli_dir, options.add_hosts) def update_console_label(console_label, consoles, cli_dir, add_hosts=False): """Update CONSOLE_LABEL on your autotest server. This removes the label and recreates it, then populating the label with all the machines your conmux server knows about. *Note If the hosts do not exist they are created. Args: console_label: string, describes the autotest label to add to machines. consoles: list, all the consoles that have confirmed console support. """ # TODO: Update to new CLI and change logic until then # this is the best way to ensure a machine is added i.e. one at a time for host in consoles: if not host_label_add(host, console_label, cli_dir): # Try to create host if add_hosts: if host_create(host, cli_dir): host_label_add(host, power_label, cli_dir) else: print "Unable to add host " + host def update_power_label(power_label, conmux_dir, cli_dir, add_hosts=False): """Look in CONSOLE_DIR/etc and grab known power commands Then remove POWER_LABEL and add machines to that label """ # remove label and add it for host in hard_reset_hosts(conmux_dir): rc = label_add_host(host, power_label, cli_dir) if not rc: # Try to create the host if add_hosts: if host_create(host, cli_dir): rc = label_add_host(host, power_label, cli_dir) else: print "Unable to add host " + host def hard_reset_hosts(conmux_dir): """Go through conmux dir and find hosts that have reset commands""" config_dir = os.path.join(conmux_dir, "etc") hosts = [] for file in os.listdir(config_dir): if not file.endswith(".cf"): continue file_path = os.path.join(config_dir, file) try: try: f = open(file_path) for line in f: if "reset" in line: hosts.append(file.rstrip(".cf")) except IOError: pass finally: f.close() return hosts def host_create(host, cli_dir): """Create a host Return: True, if successfuly false if failed """ cmd = "%s/host-create %s" % (cli_dir, host) status, output = commands.getstatusoutput(cmd) return status == 0 def label_add_host(host, label, cli_dir): """Add a host to a label""" host_cmd = "%s/label-add-hosts %s %s" % (cli_dir, label, host) (status, output) = commands.getstatusoutput(host_cmd) if status != 0: return False return True def remove_create_label(label, cli_dir): """Remove and recreate a given label""" cmd = "%s/label-rm %s" % (cli_dir, label) status, output = commands.getstatusoutput(cmd) if status != 0: raise Exception("Error deleting label: " + label) cmd = "%s/label-create %s" % (cli_dir, label) status, output = commands.getstatusoutput(cmd) if status != 0: raise Exception("Error creating label: " + label + output) return True def get_console_hosts(console_binary, conmux_server): """Use console to collect console hosts and return a list. Args: console_binary: string, location of the conmux console binary conmux_server: string, hostname of the conmux server Returns: A List of console conmux is currently running on. """ hosts_list = [] cmd = "%s --list %s" % (console_binary, conmux_server) for line in commands.getoutput(cmd).split('\n'): host = (line.split(' '))[0] hosts_list.append(host) return hosts_list def check_host(host, console_binary): """Check hosts for common errors and return the status. Args: host: string, the console host identifier console_binary: string, location of the conmux console binary Returns: int, 0: Machine state is good int, 1: Machine state is bad int, 2: Machine state is unknown """ RESPONSES = [ host + ' login:', 'ENOENT entry not found', 'login:', 'Connection refused', '<<>>', 'Authentication failure', 'Give root password for maintenance', ] cmd = '%s %s' % (console_binary, host) shell = pexpect.spawn(cmd) shell.send('\r\n') shell.send('\r\n') shell.send('\r\n') try: # May need to increase the timeout but good so far response = shell.expect(RESPONSES, 1) except pexpect.TIMEOUT: shell.sendline('~$') shell.expect('>') shell.sendline('quit') return 1 except pexpect.EOF: # unknown error shell.sendline('~$') shell.expect('>') shell.sendline('quit') return 2 # TODO: Change actions based on what server returned if response == 0: # OK response return 0 else: return 1 if __name__ == '__main__': main(sys.argv) conmux-0.12.0/INSTALL0000644000175000017500000000124411376624514012642 0ustar loolloolPrerequisites ============= This package required the following perl modules to be installed: o IO::Multiplex; Debian Packages: o libio-multiplex-perl Fedora Packages: o perl-IO-Multiplex Building ======== To make and install this package to the default location (/usr/local/conmux): # make install To an alternative location: # make PREFIX=/usr/alt/conmux install To build for a specified prefix, but installed into a temporary tree: # make PREFIX=/usr/alt/conmux BUILD=build/location install Legal ===== (C) Copyright IBM Corp. 2004, 2005, 2006 Author: Andy Whitcroft The Console Multiplexor is released under the GNU Public License V2 conmux-0.12.0/console0000755000175000017500000000675511376624514013215 0ustar loollool#!/usr/bin/perl # # console / -- interactive client interface # # The main interactive client interace to conmux. Allows direct # interaction with the payload, as well as allowing break out # to the conmux menu to envoke defined commands; for example # hardreset. # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # $| = 1; our $P = 'console'; use FindBin; use POSIX qw(errno_h BUFSIZ); use IO::Socket; use Getopt::Long qw(:config no_auto_abbrev); my $CONMUX = $FindBin::Bin; my $CONMUX = $ENV{'CONMUX_ROOT'} if ($ENV{'CONMUX_ROOT'}); # Find our internal libraries. use lib $FindBin::Bin; use Conmux; # Basic terminal handling. sub termRaw { $termSettings = `stty -g`; system "stty raw -echo opost onlret"; } sub termRestore { system "stty $termSettings"; } my $bot; my $list; my $status; GetOptions( 'b|bot=s' => \$bot, 'l|list' => \$list, 's|status' => \$status, ); sub usage { warn "Usage: $P \n"; warn " $P /\n"; warn " $P :\n"; warn " $P --status \n"; die " $P --list []\n"; } my $id; if ($bot) { $id = 'bot:' . $bot; } else { $id = 'user:' . $ENV{'LOGNAME'}; } # # MODE: registry list. # if ($list) { if ($#ARGV == -1) { print Conmux::Registry::list('-'); } elsif ($#ARGV == 0) { print Conmux::Registry::list($ARGV[0]); } else { usage(); } exit 0 } # # COMMAND: payload status command # if ($status) { usage() if ($#ARGV != 0); my $sock; eval { $sock = Conmux::connect($ARGV[0]); }; if ($@) { print "unavailable\n"; exit 0 } my %r = Conmux::sendCmd($sock, 'CONNECT', { 'id' => $id, 'to' => 'console', 'hide' => 1 }); if ($r{'status'} ne 'OK') { print "unavailable\n"; } elsif ($r{'state'}) { print "$r{'state'}\n"; } else { print "unknown\n"; } exit 0; } # # COMMAND: general payload connect. # if ($#ARGV != 0) { usage(); } # Connect to the host/port specified on the command line, # or localhost:23 my $sock = Conmux::connect($ARGV[0]); my %r = Conmux::sendCmd($sock, 'CONNECT', { 'id' => $id, 'to' => 'console' }); die "$P: $ARGV[0]: connect failed - $r{'status'}\n" if ($r{'status'} ne 'OK'); print "Connected to $r{'title'} (~\$quit to exit)\n"; #display message of the day passed by the server e.g. who is already connected print "$r{'motd'}"; my $rin = $win = $ein = ''; vec($rin, fileno(STDIN), 1) = 1; vec($rin, fileno($sock), 1) = 1; my $ein = $rin | $win; # We want to buffer output to the terminal. This prevents the program # from blocking if the user hits CTRL-S for example. # XXX ^^^ termRaw(); # Loop waiting for input from the user, or from the server. while (1) { $nfound = select($rout=$rin, $wout=$win, $eout=$ein, undef); if ($nfound < 0) { if ($! == EINTR || $! == EAGAIN) { next; } die "$P: select failed - $!\n"; } if (vec($rout, fileno(STDIN), 1)) { $len = sysread(STDIN, $data, 4096); if ($len <= 0) { if ($len < 0) { if ($! == EINTR || $! == EAGAIN) { next; } die "$P: STDIN: read failed - $!\n"; } print STDERR "Connection Closed (client)\n"; last; } print $sock $data; } if (vec($rout, fileno($sock), 1)) { $len = sysread($sock, $data, 4096); if ($len <= 0) { if ($len < 0) { if ($! == EINTR || $! == EAGAIN) { next; } die "$P: server: read failed - $!\n"; } print STDERR "Connection Closed (server)\n"; last; } print $data; } } termRestore(); conmux-0.12.0/helpers/0000755000175000017500000000000011376624514013252 5ustar loolloolconmux-0.12.0/helpers/module.mk0000644000175000017500000000065711376624514015100 0ustar loollool# (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 HELPERS:=autoboot-helper tickle-helper install:: @[ -d $(BASE)/lib/helpers ] || mkdir $(BASE)/lib/helpers for f in $(HELPERS); do \ rm -f $(BASE)/lib/helpers/$$f; \ cp -p helpers/$$f $(BASE)/lib/helpers/$$f; \ chmod 755 $(BASE)/lib/helpers/$$f; \ done conmux-0.12.0/helpers/autoboot-helper0000755000175000017500000000273111376624514016314 0ustar loollool#! /usr/bin/expect # # autoboot-helper -- automatic boot helper # # Some machines have real issues rebooting. This helper watches their # console output for the telltale signs of a reboot in progress. When # spotted this triggers an automated 'manual' hardreset. For use when # machines fail to reboot at the BIOS level. # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # set P "autoboot-numaq" log_user 0 if {$argc != 0} { puts stderr "Usage: $P" exit 1 } proc note {msg} { global P puts stderr "$P: $msg" } proc warn {msg} { global P puts stderr "$P: $msg" puts "~\$msg $msg" } set timeout -1 set likely 0 expect_user { {TEST;} { warn "test trigger detected" exp_continue } {Unmounting file systems} { note "controlled reboot in progress ..." set likely [clock seconds] exp_continue } {Unmounting local filesystems...} { note "controlled reboot in progress ..." set likely [clock seconds] exp_continue } -ex {***** REBOOT LINUX *****} { note "fsck failure occured ..." set likely [clock seconds] exp_continue } {Restarting system.} { if {$likely != 0} { warn "shutdown complete, restart indicated" puts "~\$hardreset" set likely 0 } else { warn "likely false positive" } exp_continue } "*\n" { if {$likely > 0 && ([clock seconds] - $likely) > 60} { warn "trigger timeout" set likely 0 } exp_continue } } conmux-0.12.0/helpers/tickle-helper0000755000175000017500000000373011376624514015733 0ustar loollool#! /usr/bin/expect # # tickle-helper -- watch for reboots and 'tickle' the console during them # # Some consoles get broken when the machine reboots. There are normally # fixed by trying to use them at or arround the reboot. Watch for reboots # and initiate use of the console to trigger a drop/reconnect cycle. # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # set P "tickle-helper" log_user 0 if {$argc != 0} { puts stderr "Usage: $P" exit 1 } proc note {msg} { global P puts stderr "$P: $msg" } proc warn {msg} { global P puts stderr "$P: $msg" puts "~\$msg $msg" } proc tickle {} { set timeout 5 warn "tickling console ..." puts "" set now [clock seconds] expect_user { {blade: ERROR: console lost} { } {Elapsed time since release of system processors:} { } "*\n" { if {([clock seconds] - $now) > 5} { set now [clock seconds] warn "tickling console ..." puts "" } exp_continue } timeout { set now [clock seconds] warn "tickling console ..." puts "" exp_continue } } set timeout -1 warn "tickle complete ..." } set timeout -1 set likely 0 expect_user { {TEST;} { warn "test trigger detected" exp_continue } -re {Unmounting file systems|Unmounting local filesystems...} { note "controlled reboot in progress ..." set likely [clock seconds] exp_continue } -ex {***** REBOOT LINUX *****} { note "fsck failure occured ..." set likely [clock seconds] exp_continue } -re {HARDBOOT INITIATED|initated a hard reset} { tickle exp_continue } -re {Please stand by while rebooting the system|Restarting system} { if {$likely != 0} { warn "shutdown complete, restart indicated" tickle set likely 0 } else { warn "likely false positive" } exp_continue } "*\n" { if {$likely > 0 && ([clock seconds] - $likely) > 60} { warn "trigger timeout" set likely 0 } exp_continue } } conmux-0.12.0/COPYING0000644000175000017500000004341711376624514012654 0ustar loolloolThe Console Multiplexor (C) Copyright IBM Corp. 2004, 2005, 2006 The Console Multiplexor is released under the GNU Public Licence V2 a copy of which should be included below. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. conmux-0.12.0/Conmux.pm0000644000175000017500000001057311376624514013425 0ustar loollool# # Conmux.pm -- core console multiplexor package # # Implements the core multiplexor functionality such as resolution of # names and connecting to the conmux server. # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # package Conmux; use URI::Escape; use File::Basename; use Cwd 'abs_path'; our $Config; BEGIN { my $abs_path = abs_path($0); my $dir_path = dirname($abs_path); my $cf = '/usr/local/conmux/etc/config'; if (-e "$dir_path/etc/config") { $cf = "$dir_path/etc/config"; } elsif (-e "$dir_path/../etc/config") { $cf = "$dir_path/../etc/config"; } if (-f $cf) { open(CFG, "<$cf") || die "Conmux: $cf: open failed - $!\n"; while() { chomp; next if (/^#/ || /^\s*$/ || !/=/); my ($name, $value) = split(/=/, $_, 2); $value =~ s/^"//; $value =~ s/"$//; # Substitute variables. while ($value =~ /\$([A-Za-z0-9_]+)/) { my $v = $Config->{$1}; $value =~ s/\$$1/$v/; } $Config->{$name} = $value; } close(CFG); } } sub encodeArgs { my (%a) = @_; my ($a, $n, $s); ##print "0<$_[0]> ref<" . ref($_[0]) . ">\n"; # Handle being passed references to hashes too ... $a = \%a; $a = $_[0] if (ref($_[0]) eq "HASH"); for $n (sort keys %{$a}) { $s .= uri_escape($n) . '=' . uri_escape($a->{$n}) . ' '; } chop($s); $s; } sub decodeArgs { my ($s) = @_; my (%a, $nv, $n, $v); # Decode the standard argument stream. for $nv (split(' ', $s)) { ($n, $v) = split('=', $nv, 2); $a{uri_unescape($n)} = uri_unescape($v); } %a; } sub sendCmd { my ($fh, $c, $a) = @_; my ($rs); # Send the encoded command ... print $fh $c . " " . encodeArgs($a) . "\n"; # Read the reply. $rs = <$fh>; chomp($rs); decodeArgs($rs); } sub sendRequest { my ($fh, $c, $a) = @_; my %a = { 'result' => 'more' }; # Send the encoded command ... print $fh $c . " " . encodeArgs($a) . "\n"; %a; } sub revcResult { my ($fh) = @_; my ($rs); # Read the reply. $rs = <$fh>; chomp($rs); decodeArgs($rs); } # # Configuration. # sub configRegistry { my $reg = $Config->{'registry'}; $reg = "localhost" if (!$reg); $reg; } # Connect to the host/port specified on the command line, # or localhost:23 sub connect { my ($to) = @_; my ($reg, $sock); # host:port if ($to =~ /:/) { # Already in the right form. # registry/service } elsif ($to =~ m@(.*)/(.*)@) { my ($host, $service) = ($1, $2); $to = Conmux::Registry::lookup($host, $service); # service } else { $to = Conmux::Registry::lookup('-', $to); } $sock = new IO::Socket::INET(Proto => 'tcp', PeerAddr => $to) or die "Conmux::connect $to: connect failed - $@\n"; # Turn on keep alives by default. $sock->sockopt(SO_KEEPALIVE, 1); $sock; } package Conmux::Registry; sub lookup { my ($host, $service) = @_; $host = Conmux::configRegistry() if ($host eq '-'); # Connect to the registry service and lookup the requested service. my $reg = new IO::Socket::INET(Proto => 'tcp', PeerAddr => "$host", PeerPort => 63000) or die "Conmux::connect: registry not available - $@\n"; my %r = Conmux::sendCmd($reg, 'LOOKUP', { 'service' => $service }); die "Conmux::Registry::lookup: $service: error - $r{'status'}\n" if ($r{status} ne "OK"); close($reg); $r{'result'}; } sub add { my ($host, $service, $location) = @_; $host = Conmux::configRegistry() if ($host eq '-'); # Connect to the registry service and lookup the requested service. my $reg = new IO::Socket::INET(Proto => 'tcp', PeerAddr => "$host", PeerPort => 63000) or die "Conmux::connect: registry not available - $@\n"; my %r = Conmux::sendCmd($reg, 'ADD', { 'service' => $service, 'location' => $location }); die "Conmux::Registry::add: $service: error - $r{'status'}\n" if ($r{status} ne "OK"); close($reg); 1; } sub list { my ($host, $service, $location) = @_; my (@results, %r); $host = Conmux::configRegistry() if ($host eq '-'); # Connect to the registry service and ask for a list. my $reg = new IO::Socket::INET(Proto => 'tcp', PeerAddr => "$host", PeerPort => 63000) or die "Conmux::connect: registry not available - $@\n"; %r = Conmux::sendCmd($reg, 'LIST', { }); ## while ($r{'status'} eq 'more') { ## %r = receiveResult($reg); ## push(@results, $r{'result'}); ## } die "Conmux::Registry::list: error - $r{'status'}\n" if ($r{'status'} ne "OK"); close($reg); $r{'result'}; } 1; conmux-0.12.0/conmux0000755000175000017500000006767311376624514013072 0ustar loollool#!/usr/bin/perl # # conmux -- the main console multiplexor daemon # # Main console multiplexor daemon. There is one of these daemons for # each open console supported in the system. Clients are directed to # this daemon via the conmux-registry deamon. # # (C) Copyright IBM Corp. 2004, 2005, 2006 # Author: Andy Whitcroft # # The Console Multiplexor is released under the GNU Public License V2 # use strict; use FindBin; use Symbol qw(gensym); use IO::Socket; use IO::Multiplex; use IPC::Open3; use URI::Escape; use Net::Domain; # Find our internal libraries. use lib $FindBin::Bin; use lib "$FindBin::Bin/../lib/"; use lib "$FindBin::Bin/lib/"; use Conmux; our $P = 'conmux'; our $debug = 0; $SIG{'CHLD'} = "IGNORE"; $| = 1; # # CALLBACK: this class is used to provide a timed callback. The multiplexor # libarary allows us to set a timeout on any open file we have registered. # So, we open a new file descriptor to /dev/null and set a timeout on that. # package Callback; sub new { my ($class, $mux, $who, $time) = @_; my $self = bless { 'who' => $who }, $class; my ($fh); print "Callback::new [$self] mux<$mux> who<$who> time<$time>\n" if ($main::debug); # Open a file handle to nothing, we need this to hang the timeout # on in the multiplexor. It will fail with a mux_eof, which we ignore. open($fh, "add($fh); $mux->set_callback_object($self, $fh); $mux->set_timeout($fh, $time); $self; } sub mux_timeout { my ($self, $mux, $fh) = @_; print "Callback::mux_timeout [$self] mux<$mux> fh<$fh>\n" if ($main::debug); $self->{'who'}->callback_timeout(); $mux->close($fh); } sub DESTROY { my ($self) = @_; print "Callback::DESTROY [$self]\n" if ($main::debug); } # # LISTENER SOCKET: creates an intenet listener for new clients and # connects them to the junction provided. # package ListenerSocket; sub new { my ($class, $mux, $port) = @_; my $self = bless { 'mux' => $mux }, $class; print "ListenerSocket::new [$self] mux<$mux> port<$port>\n" if ($main::debug); $self->initialise($port); $self; } sub initialise { my ($self, $port) = @_; my ($sock); print "ListenerSocket::initialise [$self] port<$port> " if ($main::debug); # Create a listening socket and add it to the multiplexor. my $sock = new IO::Socket::INET(Proto => 'tcp', LocalPort => $port, Listen => 4, ReuseAddr => 1) or die "socket: $@"; print " adding $self $sock\n" if ($main::debug); $self->mux->listen($sock); $self->mux->set_callback_object($self, $sock); $self->listener($sock); } # DATA accessors. sub mux { my $self = shift; if (@_) { $self->{'mux'} = shift } return $self->{'mux'}; } sub listener { my $self = shift; if (@_) { $self->{'listener'} = shift } return $self->{'listener'}; } sub address { my ($self) = @_; Net::Domain::hostfqdn() . ':' . $self->{'listener'}->sockport(); } # JUNCTION: callbacks. ##sub junctionInput { ##} ##sub junctionEOF { ## my ($self) = @_; ## ## $self->{'junction'}->junctionRemove($self, 'console-client'); ## $self->{'mux'}->close($self->{'listener'}); ##} # Handle new connections by instantiating a new client class. sub mux_connection { my ($self, $mux, $fh) = @_; my ($client); print "ListenerSocket::mux_connection [$self] mux<$mux> fh<$fh>\n" if ($main::debug); # Make a new client connection. $client = ClientCmd->new($mux, $fh); print " new connection $self $client\n" if ($main::debug); } sub DESTROY { my ($self) = @_; print "ListenerSocket::DESTROY [$self]\n" if ($main::debug); close($self->listener); } # # JUNCTION: generic junction box object, connects names groups of objects # to other named groups. # # Expects the following callbacks to be defined on each object registered: # junctionInput($from, $data) # junctionEOF($from, $to) # package Junction; sub new { my ($class) = @_; my $self = bless { }, $class; print "Junction::new [$self]\n" if ($main::debug); $self; } sub junctionAdd { my ($self, $client) = @_; print "Junction::junctionAdd [$self] client<$client>\n" if ($main::debug); # Add ourselves to the list of recipients. $self->{$client} = $client; } sub junctionInput { my ($self, $client, $data) = @_; my ($c); print "Junction::junctionInput [$self] client<$client> " . "data<$data>\n" if ($main::debug); # Send this data on to the clients listed in the output list. for $c (values %{$self}) { print " sending to $c\n" if ($main::debug); $c->junctionInput($client, $data); } } sub junctionEOF { my ($self, $client) = @_; my ($c); print "Junction::junctionEOF [$self] client<$client>\n" if ($main::debug); # Send this eof on to the clients listed in the output list. for $c (values %{$self}) { print " sending to $c\n" if ($main::debug); $c->junctionEOF($client); } } sub junctionRemove { my ($self, $client) = @_; print "Junction::junctionRemove [$self] client<$client>\n" if ($main::debug); # Drop this client from our lists. delete $self->{$client}; } # # PAYLOAD: generic payload object, connects itself to the requisite junction. # package Payload; my %payloads = (); my $payloads = 0; sub lookup { my ($class, $name) = @_; $payloads{$name}; } sub found { my ($class, $name, $self) = @_; print "Payloads::found name<$name> self<$self>\n" if ($main::debug); $payloads{$name} = $self; $payloads++; } sub lost { my ($class, $name, $self) = @_; print "Payloads::lost name<$name> self<$self>\n" if ($main::debug); undef $payloads{$name}; if (--$payloads == 0) { exit(0); } } sub new { my ($class, $name, $title, $mux, @a) = @_; my $self = bless { }, $class; print "Payload::new [$self] name<$name> title<$title> mux<$mux>\n" if ($main::debug); Payload->found($name, $self); $self->name($name); $self->title($title); $self->mux($mux); $self->enabled(1); $self->cin(Junction->new); $self->cout(Junction->new); $self->initialise(@a); $self; } # Data accessors. sub name { my $self = shift; if (@_) { $self->{'name'} = shift } return $self->{'name'}; } sub title { my $self = shift; if (@_) { $self->{'title'} = shift } return $self->{'title'}; } sub mux { my $self = shift; if (@_) { $self->{'mux'} = shift } return $self->{'mux'}; } sub cin { my $self = shift; if (@_) { $self->{'cin'} = shift } return $self->{'cin'}; } sub cout { my $self = shift; if (@_) { $self->{'cout'} = shift } return $self->{'cout'}; } sub enabled { my $self = shift; if (@_) { $self->{'enabled'} = shift } return $self->{'enabled'}; } sub connected { my $self = shift; if (@_) { $self->{'connected'} = shift } $self->transition(); return $self->{'connected'}; } sub transition { my $self = shift; my $time = time; if (($time - $self->{'trans_minor'}) > 30) { $self->{'trans_major'} = $time; } $self->{'trans_minor'} = $time; } sub retry_timeout { my $self = shift; my $time = time - $self->{'trans_major'}; if ($time < 60) { return 1; } elsif ($time < 120) { return 10; } else { return 30; } } sub state { my $self = shift; my $ctime = $self->{'connected'}; my $ttime = $self->{'trans_major'}; my $time = time; if ($ctime && ($time - $ctime) > 30) { "connected"; } elsif ($ttime && ($time - $ttime) < 60) { "transition"; } else { "disconnected"; } } sub initialise { my ($self) = @_; my ($sock); print "Payload::initialise [$self]\n" if ($main::debug); # Ensure we recieve client input. $self->cin->junctionAdd($self); $self->connected(time); } # Telnet constants. my $TN_IAC = sprintf("%c", 255); my $TN_DONT = sprintf("%c", 254); my $TN_DO = sprintf("%c", 253); my $TN_WONT = sprintf("%c", 252); my $TN_WILL = sprintf("%c", 251); my $TN_SB = sprintf("%c", 250); my $TN_SE = sprintf("%c", 240); my $TN_BREAK = sprintf("%c", 243); my $TNOPT_ECHO = sprintf("%c", 1); my $TNOPT_SGA = sprintf("%c", 3); # # If we get here then we have accumulated a complete telnet # negotiation string. # # Telnet negotiation protocol - RFC#854: # # DO We are being asked to DO an option # DONT We are being asked to NOT DO an option # WILL We are being told they will DO an option # WONT We are being told they will NOT DO an option # # DO/DONT requests indicate we should {en,dis}able a mode. # We are expected to respond with WILL or WONT. To prevent # loops, we should not respond if the request matches our # current mode. # # WILL/WONT requests indicate the other end would like to # {en,dis}able a mode. We are expected to respond with # DO/DONT. # # If we want a particular mode {en,dis}abled then we may start # negotiation of that mode with a WILL/WONT. # # We want the other end to perform echo by default so we will # DO any request for ECHO and DONT all other requests. # sub mux_input { my ($self, $mux, $fh, $input) = @_; my ($client); print "Payload::mux_input [$self] mux<$mux> fh<$fh> input<$$input>\n" if ($main::debug); while ($$input ne "") { # Ordinary text. if ($$input =~ s/^([^$TN_IAC]+)//) { # Data coming in from the payload, this needs to go to # all of the clients. $self->cout->junctionInput($self, $1); next; } # IAC,SB,...,SE if ($$input =~ s/^$TN_IAC$TN_SB([^$TN_SE]+)$TN_SE//) { print "SB\n" if ($main::debug); next; } # IAC,[DO|DONT|WILL|WONT], if ($$input =~ s/^$TN_IAC$TN_DO(.)//) { my $c = unpack("C", $1); print "DO<$c:$1>\n" if ($main::debug); # We are DONT on all options so WONT all requests. $self->junctionInput($self, "$TN_IAC$TN_WONT$1"); next; } if ($$input =~ s/^$TN_IAC$TN_DONT(.)//) { my $c = unpack("C", $1); print "DONT<$c:$1>\n" if ($main::debug); # We are already DONT on all options, no reply. next; } if ($$input =~ s/^$TN_IAC$TN_WILL(.)//) { my $c = unpack("C", $1); print "WILL<$c:$1>\n" if ($main::debug); my $reply = $TN_DONT; if ($1 == $TNOPT_ECHO || $1 == $TNOPT_SGA) { $reply = $TN_DO; } $self->junctionInput($self, "$TN_IAC$reply$1"); next; } if ($$input =~ s/^$TN_IAC$TN_WONT(.)//) { my $c = unpack("C", $1); print "WONT<$c:$1>\n" if ($main::debug); $self->junctionInput($self, "$TN_IAC$TN_DONT$1"); next; } # IAC,