#!/bin/sh  
# \
exec oagwish "$0" "$@"

set auto_path [linsert $auto_path 0  /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
set apsttk 1
APSDebugPath

if {[catch {package require Expect} results]} {
    APSAlertBox [APSUniqueName .] -errorMessage "$results"
    exit 1
}

set CVSRevisionAuthor "\$Revision: 1.24 $ \$Author: soliday $"

set usage "usage: NetProcessControl -title <string>\
    -programName <string> \[-matchList <string-list>\] \[-hostList <string-list>\]"
set args $argv
set title NetProcessControl
set programName ""
set matchItems ""
set excludeItems ""
set hostList [lsort -unique {ariel arrow artemus aurelius aurelius2 auric cadmus \
    chaos charis cyclops demeter echo gladiator gladiator2 herema juno \
    maximus maximus2 mcrtest medusa phoenix schaumburg talos}]

if {[APSStrictParseArguments {title programName matchItems excludeItems hostList}] || \
        ![string length $title]} {
    APSAlertBox [APSUniqueName .] -errorMessage "$usage"
    exit 1
}

set hostList [lsort $hostList]
foreach elem $hostList {
    set $elem 1
}
set aurelius 0
set gladiator 0
set maximus 0
set silverado 0

proc ScanForProcesses {args} {
    global hostList programName matchItems excludeItems w scanAllProcesses tcl_platform
    eval global $hostList

    set matchOption ""
    if [string length $matchItems] {
        set first 1
        foreach item [split $matchItems ,] {
            if $first {
                set first 0
                set matchOption "-match=column,Args=*${item}*"
            } else {
                set matchOption "$matchOption,Args=*${item}*,|"
            }
        }
    }
    
    set excludeOption ""
    if [string length $excludeItems] {
        set first 1
        foreach item [split $excludeItems ,] {
            if $first {
                set first 0
                set excludeOption "-match=column,Args=*${item}*"
            } else {
                set excludeOption "$excludeOption,Args=*${item}*,|"
            }
        }
        set excludeOption "$excludeOption,!"
    }

    set matchProgram ""
    if {$scanAllProcesses} {
	if [string length $programName] {
	    set first 1
	    foreach item [split $programName ,] {
		if $first {
		    set first 0
		    set matchProgram "-match=column,Args=*${item}*"
		} else {
		    set matchProgram "$matchProgram,Args=*${item}*,|"
		}
	    }
	}
    }
    set tmpFileList ""
    set tmpRoot /tmp/[APSTmpString]
    foreach host $hostList {
        set tmpFile $tmpRoot.$host
        if ![subst \$$host] continue
        APSSetVarAndUpdate status "Scanning $host"
	if {$tcl_platform(os) == "SunOS"} {
	    if [catch {exec ping $host 2} result] {
		APSSetVarAndUpdate status "Problem pinging $host: $result"
		continue
	    }
	    if [string compare $result "$host is alive"]!=0 {
		APSSetVarAndUpdate status "$host is not responding"
		continue
	    }
	} else {
	    if [catch {exec ping -c 1 -W 2 $host} result] {
		APSSetVarAndUpdate status "Problem pinging $host: $result"
		continue
	    }
	}
	global timeout expect_out password
	if {![llength $password]} {
	    APSSetVarAndUpdate status "Please supply a password"
	    return
	}
	set timeout 10
	spawn -noecho ssh -n $host ps -ed -o user=20 -o pid=20 -o args=100
	match_max -i $spawn_id 100000
	log_user 0
	expect {
	    "Are you sure you want to continue connecting (yes/no)?" {	
		exp_send "yes\r"
		exp_continue
            } "list of known hosts." {
		exp_send "yes\r"
		exp_continue
            } "continue connecting" {
		exp_send "yes\r"
		exp_continue
	    } "password" {
		exp_send "${password}\r"
		exp_continue
	    } "Password" {
		exp_send "${password}\r"
		exp_continue
	    } "Authentication failed" {
		APSSetVarAndUpdate status "Authentication failed for $host"
		catch {exp_close}
		continue
	    } timeout {
		APSSetVarAndUpdate status "connection to $host timed out"
		catch {exp_close}
		continue
	    } eof {
	    }
	}
#puts $expect_out(buffer)
	set i [string first $password $expect_out(buffer)]
	if {$i != -1} {
	    set expect_out(buffer) [string replace $expect_out(buffer) $i [expr $i + [string length $password]]]
	}
	set i [string first \" $expect_out(buffer)]
	while {$i != -1} {
	    set expect_out(buffer) [string replace $expect_out(buffer) $i $i]
	    set i [string first \" $expect_out(buffer)]
	}
	set expect_out(lines) [split $expect_out(buffer) \n]
	set expect_out(buffer) ""
	foreach line $expect_out(lines) {
#puts $line
	    if {[llength $line] > 3} {
		if {([lindex $line 0] != "20") && ([lindex $line 0] != "Connection")} {
		    set line "[lindex $line 0] [lindex $line 1] \"[lrange $line 2 end]\""
		    lappend expect_out(buffer) $line
		}
	    } elseif {[llength $line] == 3} {
		if {([lindex $line 0] != "20") && ([lindex $line 0] != "Connection")} {
		    set line "[lindex $line 0] [lindex $line 1] [lindex $line 2]"
		    lappend expect_out(buffer) $line
		}
	    }
	}
	set expect_out(buffer) [join $expect_out(buffer) \n]
	file copy -force /home/helios/OAG/oagData/controllaw/NetProcessControl.header $tmpFile
	exec echo $expect_out(buffer) \
	    | grep -v defunct >> $tmpFile

	if {$scanAllProcesses} {
	    if [catch {eval exec sddsprocess $tmpFile $tmpFile.sdds \
			   -nowarning \
			   -print=column,Host,$host \
			   $matchProgram $matchOption \
			   $excludeOption} result] {
		APSSetVarAndUpdate status "Problem scanning $host: $result"
	    } else {
		catch {file delete -force $tmpFile}
		lappend tmpFileList $tmpFile.sdds
	    }
	} else {
	    if [catch {eval exec sddsprocess $tmpFile $tmpFile.sdds \
			   -nowarning \
			   -print=column,Host,$host \
			   "-match=column,Args=*${programName}*" $matchOption \
			   $excludeOption} result] {
		APSSetVarAndUpdate status "Problem scanning $host: $result"
	    } else {
		catch {file delete -force $tmpFile}
		lappend tmpFileList $tmpFile.sdds
	    }
	}
    }
    if {![llength $tmpFileList]} return
    if [catch {eval exec sddscombine $tmpFileList -merge $tmpRoot.all} result] {
        APSSetVarAndUpdate status "Problem combining files: $result"
        return
    }
    catch {eval file delete -force $tmpFileList}

    if [catch {exec sdds2stream -rows $tmpRoot.all | token -n=1} processes] {
	catch {file delete -force $tmpRoot.all}
        APSSetVarAndUpdate status "Problem counting processes: $processes"
        return
    }
    APSSetVarAndUpdate status "$processes processes found."
    if [expr !$processes] return
    if {[catch {sdds open $tmpRoot.all r} SDDSfd]} {
	catch {file delete -force $tmpRoot.all}
        APSSetVarAndUpdate status "Problem reading data file: $SDDSfd."
        return
    }
    foreach column {Host Args PID User} {
        global ${column}List
        if [catch {sdds getColumn $SDDSfd $column} results] {
            APSSetVarAndUpdate status "Problem reading column $column: $results"
            return
        }
	set ${column}List $results
    }
    catch {sdds close $SDDSfd}
    catch {file delete -force $tmpRoot.all}

    global LabelList
    set LabelList ""
    for {set i 0} {$i<$processes} {incr i} {
	lappend LabelList [format "%10s %10s %10s %50s" \
			       [lindex $HostList $i] \
			       [lindex $UserList $i] \
			       [lindex $PIDList $i] \
			       [lindex $ArgsList $i] ]
    }
    set w [APSUniqueName .]
    APSScrolledListWindow $w \
	-name "ProcessSelectionWindow" -itemList $LabelList \
	-clearButton 1 -acceptButton 0 -selectionVar listSelection \
	-autoAccept 1
    APSDialogBoxAddButton .kill -parent $w \
	-text Kill -command "KillProcess"
}

proc KillProcess {} {
    global listSelection HostList ArgsList PIDList UserList LabelList
    global apsScriptUser w
    set procHostList ""
    foreach item $listSelection {
        set index [lsearch $LabelList $item]
	set userID [lindex $UserList $index]
	set processID [lindex $PIDList $index]
	set cHost [lindex $HostList $index]

        if [string compare $userID $apsScriptUser] {
            APSSetVarAndUpdate status "Not owner---can't kill.  Owner is $userID. You are $apsScriptUser"
            continue
        }
        if {[lsearch -exact $procHostList $cHost] < 0} {
             set processArray($cHost) $processID
	     lappend procHostList $cHost
        } else {
             lappend processArray($cHost) $processID
        }
        APSSetVarAndUpdate status "Killing process $processID on $cHost."
    }

    foreach selectHost [array names processArray] {

	global timeout expect_out password
	if {![llength $password]} {
	    APSSetVarAndUpdate status "Please supply a password"
	    return
	}
	set timeout 10
	spawn -noecho ssh -n $selectHost kill -9 $processArray($selectHost)
	log_user 0
	expect {
	    "Are you sure you want to continue connecting (yes/no)?" {	
		exp_send "yes\r"
		exp_continue
	    } "password" {
		exp_send "${password}\r"
		exp_continue
	    } "Password" {
		exp_send "${password}\r"
		exp_continue
	    } "Authentication failed" {
		APSSetVarAndUpdate status "Authentication failed for $selectHost"
		catch {exp_close}
		continue
	    } timeout {
		APSSetVarAndUpdate status "connection to $selectHost timed out"
		catch {exp_close}
		continue
	    } eof {
		
	    }
	}

	set processArray($selectHost) ""
    }

    if [info exists processArray] {
        unset processArray
    }
}

proc getTimeSeriesData {} {
    global timeSeries
    if {[catch {sdds load /home/helios/oagData/dataLoggerConfig/timeSeries.config timeSeries} results]} {
	return -code error $results
    }
    set timeSeries(Column.rootname) [lreplace $timeSeries(Column.rootname) 0 0 "[lindex $timeSeries(Column.rootname) 0] SRrfFast SRFastLog1"]
    set timeSeries(Column.monitorProgram) [lreplace $timeSeries(Column.monitorProgram) 0 0 "[lindex $timeSeries(Column.monitorProgram) 0] sddslogger sddsmonitor"]
    set timeSeries(Column.workstation) [lreplace $timeSeries(Column.workstation) 0 0 "[lindex $timeSeries(Column.workstation) 0] ravel charis"]

}

proc selectTimeSeriesData {name click} {
    global timeSeries programName hostList matchItems
    set i 0
    set index -1
    foreach rootname [lindex $timeSeries(Column.rootname) 0] {
	if {$rootname == $name} {
	    set index $i
	    break
	}
	incr i
    }
    if {$index == -1} {return}
    set programName [lindex [lindex $timeSeries(Column.monitorProgram) 0] $index]
    foreach host $hostList {
	global $host
	set $host 0
    }
    set host [lindex [lindex $timeSeries(Column.workstation) 0] $index]
    set $host 1
    set matchItems $name
}

proc ScanAllProcesses {} {
    global right timeSeries password
    global scanAllProcesses
    if {![llength $password]} {
	APSSetVarAndUpdate status "Please supply a password"
	return
    }
    set count [llength [lindex $timeSeries(Column.rootname) 0]]
    set scanAllProcesses 1

    global hostList programName matchItems excludeItems
    foreach host $hostList {
	global $host
	set $host 0
    }
    foreach host [lindex $timeSeries(Column.workstation) 0] {
	set $host 1
    }
    set programName [join [lsort -unique [lindex $timeSeries(Column.monitorProgram) 0]] ","]
    set matchItems [join [lsort -unique [lindex $timeSeries(Column.rootname) 0]] ","]
    set excludeItems ""
    update
    ScanForProcesses

    set scanAllProcesses 0
}

APSApplication . -name $title -version $CVSRevisionAuthor \
  -overview "Allows searching for and selective killing of processes running on MCR workstations."

set status Ready.
APSScrolledStatus .status -parent .userFrame -textVariable status \
        -width 60 -height 6 \
        -packOption "-fill both -expand false"
APSFrameGrid .fg -parent .userFrame -xList {left right}
set right .userFrame.fg.right
set left .userFrame.fg.left
pack configure .userFrame.fg.left -expand false -fill both
pack configure .userFrame.fg.right -expand true -fill both

APSCheckButtonFrame .hosts -parent $left -label "Host choices" \
  -variableList $hostList -buttonList $hostList -orientation vertical -limitPerRow 12 \
  -allNone 1 -contextHelp "Choose which hosts (i.e., workstations) you want to scan."

APSLabeledEntry .program -parent $right -label "Program name:" \
    -textVariable programName -width 20
APSLabeledEntry .match -parent $right -label "Match items:" \
    -textVariable matchItems -width 20 -contextHelp \
    "Enter a comma-separated list of strings to match against the commandline arguments for the program."
APSLabeledEntry .exclude -parent $right -label "Exclude items:" \
    -textVariable excludeItems -width 20 -contextHelp \
    "Enter a comma-separated list of strings to match against the commandline arguments for the program.  Commands including one or more of these strings will be excluded."

set password ""
APSLabeledEntry .password -parent $right -label "Password:" \
    -textVariable password -width 20 -contextHelp \
    "Enter your password so that ssh can connect to the different computers."
$right.password.entry configure -show "*"

pack [label $right.logLabel -text Loggers]

if {[catch {getTimeSeriesData} results]} {
    set status $results
    APSScrolledList .loggers -parent $right -height 5 -itemList ""
} else {
    APSScrolledList .loggers -parent $right -height 5 \
	-selectMode single -callback selectTimeSeriesData \
	-itemList [lindex $timeSeries(Column.rootname) 0]
}

APSButton .scan -parent .userFrame -text Scan -command ScanForProcesses -width ""
APSButton .scanall -parent .userFrame -text "Scan All Loggers" -command ScanAllProcesses  -width ""

set scanAllProcesses 0

update

