LDAS logo
TclDOC logo

The cmonClient.in Script

Modification Date: 11/20/2008

Table of Procedures

red ball cmonClient::NoOpReply
red ball cmonClient::helpTips
red ball cmonClient::helpTips
red ball cmonClient::init
red ball cmonClient::saveState
red ball cmonClient::sendNoOp
red ball cmonClient::useGlobusToggle
red ball debugPuts
red ball extractLDASrsc
red ball setupTmp

#! /bin/sh
# the next line restarts using wish \ exec wish "$0" "$@" This is the Laser Interferometer Gravitational Oservatory (LIGO) Control and Monitor client Tcl Script.
This is the main script called by cntlmon client to bring in the cntlmon client packages which have their own namespaces.
This module sources the following sub-modules:
  1. html.tcl ( html rendering functions for display window )
  2. bltgraph.tcl ( interface to BLT for graphs )
  3. b64.tcl ( base64 encode and decode )
  4. gpstime.tcl ( gps and utc time conversions )
  5. cmonCommon.tcl (global procs for screens, sockets and functions)
  6. cmonCommon2.tcl (global procs for screens and functions)
  7. cmonAdmin.tcl ( screen processing for managing ldas user accounts )
  8. cmonNodes.tcl ( screen processing for add/delete of nodes )
  9. cmonBeowulfUsers.tcl ( screen processing for showing wrapper jobs )
  10. cmonBwatch.tcl (screen processing for monitoring beowulf nodes)
  11. cmonCores.tcl ( screen processing for core file management )
  12. cmonJobs.tcl (screen processing for LDAS job queue modification)
  13. cmonLogs.tcl (screen processing for LDAS log filters)
  14. cmonLoad.tcl (screen processing for LDAS machine loads)
  15. cmonMPIjobs.tcl (screen processing for MPI job queue modification)
  16. cmonResource.tcl ( screen processing for modification of resource variable values )
  17. cmonStatistics.tcl ( screen processing for job and database statistics )
  18. cmonStatus.tcl ( screen processing for API status )
  19. cmonNodesStat.tcl ( screen processing for node usage )
  20. cmonClient.rsc ( resource file for configurable variables )
  21. cmonAPImem.tcl ( screen processing for API memory usage )
  22. cmonNodeStat.tcl ( screen processing for MPI nodes usage )
  23. cmonAPItimes.tcl ( screen processing for time spent by jobs in APIs )
  24. cmonNodesStat.tcl ( graph of node usage and histogram )
  25. cmonJobSummary.tcl ( tree view of job usage breakdown by command and user )
  26. cmonLDASTest.tcl ( panel of tests to submit as jobs to ldas )
  27. cmonUtils.tcl ( miscellaneous control functions )
  28. cmonCacheView.tcl ( diskcache frame file times )
  29. cmonLoadSummary.tcl ( beowulf cluster load summary )
  30. cmonGlobus.tcl ( authenication via globus toolkit )
Request calls are made from client handler namespace in cntlmonClient.tcl.
Some commands assumed that they are being called by the client handler.
scrolledMessageDialog Description a text window to display long messages with vertical scroll wraps horizontal Parameters none Usage Comments:
this proc must be here since cmonClient does not have all procs loaded yet to call returns choices of type e.g. ok, cancel, close etc.
proc scrolledMessageDialog { msg { type ok } { title "" } } {
	if	{ [ catch {
    	regsub -all {\n+} $msg "\n" msg
        set dlg [ toplevel .smd[ clock seconds ] -relief ridge ]
        set var ::[ string range $dlg 1 end ]
        set sw [ScrolledWindow $dlg.sw -relief raised -borderwidth 0 -auto both]
    	set subf [$sw getframe]
		set statusw $subf.status
    	set textw [ text $statusw -bg PapayaWhip -wrap word -tabs "0.5i left"  ]
		$sw setwidget $statusw
		pack $sw -fill both -expand 1
        $textw delete 0.0 end
        $textw insert end $msg
        set frame [ frame $dlg.f -relief groove ]
        foreach type $type {
       		set  but  [ Button ${frame}.$type -text $type -command "set $var $type; destroy $dlg" ]
            pack $but -side left -padx 10
        }
        wm title $dlg $title
        ;## dont expand the frame
        pack $frame -side top -anchor center
        # bind $dlg <Destroy> [ list set $var $type ]
        set old [ focus -displayof $dlg ]
        set focus $dlg
        tkwait visibility $dlg
        catch { grab $dlg }
        tkwait variable $var
        catch { grab release $dlg }
    } err ] } {
    	puts "scrolledMessageDialog: $err"
        catch { unset $var }
    	return -code error $err
    }
    set choice [ set $var ]
    unset $var
    return $choice
}

§   §   §
createNativeTextWin Description create native text window Parameters none Usage Comments:
proc createNativeTextWin { text { tag "" }  } {
	if	{ [ catch {
        if  { ! [ string length $tag ] } {
            set tag [ clock seconds ]
        }
    	set parent .nativeTextWin$tag
    	toplevel $parent
    	set f1 [ frame $parent.f1 ]
        catch { unset ::${f1}(ack) }
        set ftext [ frame $f1.ftext ]
        set log [ text $ftext.log -width 100 -height 20 \
        	-borderwidth 2 -relief raised \
            -yscrollcommand [ list $ftext.scroll set ] ]
        set scroll [ scrollbar $ftext.scroll -command [ list $log yview ] ]
        set f2 [ frame $f1.f2 ]
        set butok [ button $f2.ok -text [ set ::cmonClient::rscTextUpdate ] \
            -command "set ::${f1}(ack) 0" -width 25 ]
        set butcancel [ button $f2.cancel -text [ set ::cmonClient::rscTextNoUpdate ] \
            -command "set ::${f1}(ack) 1" -width 25 ]
        pack $scroll -side right  -fill y
        pack $log -side left -fill both -expand true
        pack $ftext -side top -fill both -expand true
        pack $butok $butcancel $butignore -side left -padx 10 -fill x -anchor s
        pack $f2 -side bottom -fill x -expand true
        pack $f1 -side top -fill both -expand true
        pack $f2 -side bottom -fill x -expand true
        pack $f1 -side top -fill both -expand true
        $log insert end $text
        vwait ::${f1}(ack)
    } err ] } {
    	return -code error $err
    }
    set ack [ set ::${f1}(ack) ]
    destroy $parent
    unset ::${f1}(ack)
    return $ack
}

§   §   §

Name: debugPuts

Description:
check if user is a priviledged user for globus sockets Usage:
  returns 1 -  priviledged user
Comments:
proc debugPuts { msg } {
	
	if	{ $::DEBUG > 0 } {
    	if	{ [ info exist ::logfd ] } {
    		puts $::logfd $msg
        	flush $::logfd
        }
    }
}

§   §   §

Name: extractLDASrsc

Description:
save state info Usage:

Comments:

running as user ldas, extract globus vars from resource files
proc extractLDASrsc {} {
    set globusvars [ list ::X509_USER_KEY ::X509_USER_CERT ::X509_CERT_DIR ::GSI_AUTH_ENABLED ]
    foreach var $globusvars {
    		catch { exec grep $var /ldas_outgoing/LDASapi.rsc } err
        	if	{ [ regexp "set $var" $err ] } {
            	eval $err
                debugPuts $err
            } else {
            	puts stdout "Error getting $var: $err"
            }
    }
    ;## get the server port
    set globusvars [ list ::TCLGLOBUS_PORT ::SERVICE_NAME ]
    foreach var $globusvars {
    	catch { exec grep $var /ldas_outgoing/cntlmonAPI/LDAScntlmon.rsc } err
    	if	{ [ regexp "set $var" $err ] } {
        	eval $err
            debugPuts $err
    	}
    }
}

§   §   §
#********************************************************
Name: setupTmp

Description:
creates the tmpdir to hold remote files staged from server
Parameters: Usage:

Comments:

proc setupTmp { clientdir } {
	catch { file delete -force $::TMPDIR }
	file mkdir $::TMPDIR
    eval file copy [ glob $::GIFDIR/*.gif ] $::TMPDIR
    #eval file copy [ glob $clientdir/*.css ] $::TMPDIR
    #puts $::logfd "css files in tmp [ glob -nocomplain $::TMPDIR/*css ]"
    flush $::logfd
}

§   §   §
rscDiff
Description: 06/25/08 per Ed Maros requirement of diffing rsc
Diff only if local rsc is older than version of cmonClient using Once user has checked the diffs and refuses them, dont prompt again until the next newer version Parameters Usage Comments:
show diffs if last diff version is lower than current version ::RSC_LAST_DIFF_VERSION is in ~/.cmonClient.state
proc rscDiff { localRsc currentRsc } {
    set dodiffs 0
    if  { [ info exist ::RSC_LAST_DIFF_VERSION ] } {
        set diffVersion [ split $::RSC_LAST_DIFF_VERSION . ]
        set thisVersion [ split $::version . ]
	    set i 0
	    foreach num $diffVersion {
		    set num2 [ lindex $thisVersion $i ]
            if  { $num == $num2 } {
                set dodiffs [ expr $dodiffs | ( 0x0 << $i )  ]
            } elseif { $num2 > $num } {
			    set dodiffs [ expr $dodiffs | ( 0x1 << $i )  ]
		    } else {
			    set dodiffs [ expr $dodiffs | ( 0x0 << $i ) ]
		    }
		    incr i 1
	    }
    } else {
        set dodiffs 1
    }
    if  { !$dodiffs } {
        set ::RSC_LAST_DIFF_VERSION $::version
        return 0
    }
    if  { [ catch {
    	    catch { exec diff -w $localRsc $currentRsc } diffs
            regsub {child process exited abnormally} $diffs {} diffs
		    if	{ [ string length $diffs ] } {
                set win .cmonClientRscDiff
        	    if	{ ! [ winfo exist $win ] } {
                    set tkdiff  [ auto_execok tkdiff ]
                    if  { [ string length $tkdiff ] } {
                        catch { exec $tkdiff $localRsc $currentRsc & } tkdiff_pid
                        after 500
                        set  ack [ tk_dialog $win "Resource File update" [ set ::cmonClient::rscTextPrompt ] \
                            questhead 0 [ set ::cmonClient::rscTextUpdate ] \
                            [ set ::cmonClient::rscTextNoUpdate ] ]
                        catch { exec kill -9 $tkdiff_pid }
                    } else {
                        set msg "cmonClient.rsc has been changed:\nlocal vs current:\n$diffs\n\
			         Would you like to update to the current one?"
                    ;## need to be native
                        set ack [ createNativeTextWin $msg ]
                    }
			        if	{ [ string equal 0 $ack ] } {
				        file copy -force $currentRsc $localRsc
				        puts $::logfd "resource file $localRsc updated from $currentRsc"
			        } else {
				        puts $::logfd "Resource not updated, using local resource file $localRsc"
			        }
                    set ::RSC_LAST_DIFF_VERSION $::version
                }
            }
    } err ] } {
        puts $::logfd $err
        return -code error $err
    }
}

§   §   §

Name::runMode
Description determine where the executable files reside Parameters none Usage Comments:
proc runMode {} {
	set ::statefile [ file join $::env(HOME) .cmonClient.state ]
	if	{ [ file exist $::statefile ] } {
		source $::statefile
	}
	if	{ [ info exist ::env(LDAS) ] } {
		set ::LDAS $::env(LDAS)
	} else {
		set ::LDAS /ldas
	}
	if	{ [ regexp {(^$::LDAS|stow_pkgs/ldas)} [ pwd ] ] } {
        set ack [ tk_messageBox -type ok -default ok \
			-message "running in the installation directory is not allowed \
            as cmonClient write temporary files here.\
		    Please change to a working directory and restart cmonClient." -icon error ]
		handleExit
	}
	set clientdir .
	set mode local
	
	;## explicit user path overrides env
	if	{ [ regexp {^\./cmonClient} $::argv0 ] } {
		set clientdir .
	} elseif { [ regexp {[/]+} $::argv0 ] } {
		set clientdir [ file dir $::argv0 ]
	} else {
		set clientdir [ file dir [ auto_execok cmonClient ] ]
	}
	;## if invoked in path or running from ldas directory
    ;## if user is ldas, then run mode is ldas
    ;## else user is local
    if	{ [ string equal ldas $::tcl_platform(user) ] } {
		set mode ldas
	} else {
		set mode local
	}
    set ::RUNMODE $mode
    if	{ [ regexp /ldas/bin $clientdir ] } {
    	set mode ldas_usr
    }
	if	{ [ string match ldas* $mode ] } {
		set clientdir [ file join $::LDAS lib cmonClient ]
	}
	
	if	{ [ info exist ::env(EXTRALIB) ] } {
		set extralibs [ set ::env(EXTRALIB) ]
	} else {
		set extralibs ""
	}
	puts $::logfd "Extra tcl libraries=$extralibs"
	if	{ [ string match ldas* $mode ] } {
		set ::GIFDIR $::LDAS/doc/gifs
		set ::LEAPSECSFILE [ file join $::LDAS lib cmonClient leapseconds ]
		file copy -force $::LEAPSECSFILE .
		set ::auto_path "$::LDAS/lib $::auto_path $extralibs "
		source [ file join $::LDAS lib cmonClient ldas_version.tcl ]
        source [ file join $::LDAS doc db2 doc text dbtables.tcl ]
        set ::LDASCMDDIR [ file join $::LDAS share ldas ldascmds ]
		set ::HTML2PS_CFG [ file join $::LDAS lib cmonClient html2ps.cfg ]
	} else {
		set ::GIFDIR $clientdir/gifs
		set ::LEAPSECSFILE [ file join $clientdir leapseconds ]
		file copy -force $::LEAPSECSFILE .
		set ::auto_path "$clientdir $::auto_path $extralibs"
		source [ file join $clientdir ldas_version.tcl ]
        source [ file join $clientdir dbtables.tcl ]
        set ::LDASCMDDIR [ file join $clientdir ldascmds ]
		set ::HTML2PS_CFG [ file join $clientdir html2ps.cfg ]
	}
	set currentRsc [ file join $clientdir cmonClient.rsc ]
	set ::rscfile $currentRsc
	set localRsc  [ file join $::env(HOME) cmonClient.rsc ]
	set ::rscfile $localRsc
	if  { ! [ file exists $localRsc ] } {
	 	file copy $currentRsc $::env(HOME)
	} else {
        rscDiff $localRsc $currentRsc
    }
    if	{ [ info exist ::tcl_platform(wordSize) ] && $::tcl_platform(wordSize) == 8 } {
    	set pattern "/ldcg/lib/64|/ldcg/lib64"
    } else {
    	set pattern "/ldcg/lib"
    }
    if	{ ! [ regexp $pattern $::auto_path ] } {
       	if	{ [ file exist /ldcg/lib ] } {
           	set ::auto_path "/ldcg/lib/64 /ldcg/lib64 /ldcg/lib $::auto_path"
        }
    }
    puts $::logfd "auto_path=$::auto_path"
    set ::dbtables $::Tables
    scan $::version "0.%f" ::versionF
	puts $::logfd "configure file for html2ps is $::HTML2PS_CFG"
	uplevel #0 {
		puts $::logfd "sourcing $::rscfile"
		source $::rscfile
	}
	if	{ $::DEBUG } {
		set text  "LDAS set to $::LDAS, version $::version\n"
		append text "client executable directory is $clientdir\n"
		append text "setting auto_path to $::auto_path\n"
		append text "setting leapseconds file to $::LEAPSECSFILE\n"
		append text "setting gif directory ::GIFDIR to $::GIFDIR\n"
		append text "running $::argv0 in [ pwd ]\n"
        append text "setting ::PUBDIR to $::PUBDIR\n"
		puts $::logfd $text
        flush $::logfd
	}
    ;## set TMPDIR for this session
    set ::TMPDIR ${::TMPDIR}[pid]
    puts $::logfd "Temp directory $::TMPDIR\nrunmode $mode\nclientdir $clientdir"
    flush $::logfd
    setupTmp $clientdir
    ;## update tclglobus vars if runmode is ldas
    if	{ [ string match ldas $mode ] } {
    	extractLDASrsc
    }
}

§   §   §
namespace eval cmonClient { set mainframe "" set status "" set prgtext "" set prgindic "" set font "" set font_name "" set toolbar1 1 set toolbar2 1 set sid {} set client new set NOSERVER no-server set site "" set sites [ concat no-server [ array names ::siteport ] ] set oldsite no-server set server "" set state 0 set display "" set cookie "" set childPids [ list ] ;## pages to load up set pages { cmonLDAS "LDAS control" \ cmonStatus "LDAS status" \ cmonLoad "System Load" \ cmonAPImem "APIs" \ cmonJobs "LDAS job control" \ cmonLogs "LDAS log filter" \ cmonMPIjobs "MPI job control" \ cmonNodes "Beowulf Nodes" \ cmonLDASTest "LDAS Test" \ cmonDBview "Database View" \ cmonCacheView "Cache View" \ cmonMountPts "Mount Points" } set notConnected [ format %-40.40s "Not connected to cntlmon API" ] set whostName .tmpHostName set emergSock "" set noPass 0 set serverKey nokey set pageUpdate "" set rscTextUpdate "Yes, update resource" set rscTextNoUpdate "No, skip update for now" set rscTextPrompt "Resource File has changed: would you like to update to the current changes ?" }
§   §   §
set ::logfd [ open cmonClient[pid].log w ] wm withdraw . runMode set rc [ catch { package require -exact BWidget 1.7 } ] if { $rc } { puts stderr "Unable to locate package BWidget 1.7, exiting" exit }
§   §   §
set ::BWversion [ lindex [ package version BWidget ] end ] package require plot 1.0 package require blt_graph package require -exact ldashtml 1.0 package require gpstime 1.0 package require base64 1.0 package require -exact counter 2.0 package require cmonCommon 1.0 package require cmonLoad 1.0 package require cmonLogs 1.0 package require cmonStatus 1.0 package require cmonJobs 1.0 package require cmonLDAS 1.0 package require cmonMPIjobs 1.0 package require cmonBwatch 1.0 package require cmonTree 1.0 package require cmonBeowulfUsers 1.0 package require cmonAdmin 1.0 package require cmonCommon2 1.0 package require cmonCores 1.0 package require cmonStatistics 1.0 package require cmonNodes 1.0 package require cmonResource 1.0 package require cmonUtils 1.0 package require cmonAPImem 1.0 package require cmonNodesStat 1.0 package require cmonAPItimes 1.0 package require cmonLDASTest 1.0 package require cmonBlock 1.0 package require cmonDBview 1.0 package require cmonJobSummary 1.0 package require cmonCacheView 1.0 package require cmonLoadSummary 1.0 package require cmonNodesUsers 1.0 package require cmonMountPts 1.0 package require cmonGlobusChannel 1.0 cmonClient::create_intro Description creates the intro page while loading the main gui pages.
Parameters none Usage Comments:
proc cmonClient::create_intro { } {
    global tcl_platform
    set top [toplevel .intro -relief raised -borderwidth 2]
    wm withdraw $top
    wm overrideredirect $top 1
    set im [ image create photo -file $::LDASgif  ]
    set ximg  [label $top.x -bitmap @$::LDASxbm \
        -foreground blue -background blue ]
    set bwimg [label $ximg.bw -image $im -relief ridge -borderwidth 20 ]
    set frame [frame $ximg.f -background white -relief sunken -borderwidth 3 ]
    set lab1  [label $frame.lab1 -text "Loading LDAS control/monitor" \
		-background #a2befe -font {times 12} -width 40 ]
    set lab2  [label $frame.lab2 -textvariable ::cmonClient::prgtext \
		-background #cec6ca -font {times 12} -width 40 ]
    set prg   [ProgressBar $frame.prg -width 80 -height 10 -background white \
                   -variable ::cmonClient::prgindic -maximum 20]
    pack $lab1 $lab2 $prg
	pack $bwimg $frame
    pack $ximg
	;## the following got window up the edge
    ;## BWidget::place $top 0 0 center
    BWidget::place $top 0 0
    wm deiconify $top
}

§   §   §
cmonClient::create Description creates the intro page while loading the main gui pages.
Parameters none Usage Comments:
proc cmonClient::create {} {
    global   tcl_platform
    global   tk_patchLevel
    set ::cmonClient::prgindic -1
	;##set ::cmonClient::client "new $::cmonClient::display,$::cmonClient::cookie"
    set ::cmonClient::client "new"
	
    create_intro
	set ::image_tips [ image create photo -file $::GIFDIR/reminder.gif ]
	
    update
    set ::cmonClient::prgtext   "Creating toplevel pages..."
    set ::cmonClient::prgindic  0
    set mainframe [MainFrame .mainframe \
                       -textvariable cmonClient::status \
                       -progressvar  cmonClient::prgindic]
					
    # toolbar 1 creation
    incr ::cmonClient::prgindic 1
    set tb1  [$mainframe addtoolbar]
	;## separate frames so the update site and exit buttons wont shift up
	;## when server menu button is re-created.
	
	set f1 [ frame $tb1.f1 ]
    label $f1.labf1 -text "Select controlmon server at: "  -relief flat
	pack  $f1.labf1 -anchor w -side left -padx 5 -pady 1
	cmonClient::createSiteOptions $f1.site
	set f2 [ frame $tb1.f2 ]
    set passwdWidget [ createPasswdWidget $f2 ]
    set ck1  [ checkbutton $f2.globus -text "Globus" -variable ::USE_GLOBUS \
        	-font $::LISTFONT -onvalue 1 -offvalue 0 -command "cmonClient::useGlobusToggle $f2.globus" ]
    if  { [ info exist ::env(GLOBUS_LOCATION) ] } {
        catch { cmonGlobusChannel::init } err
    }
    set ::cmonClient::globusToggle $ck1
    set ::cmonClient::showGlobusInfo 0
    ;## toolbar2 creation ( user preferences )
    incr ::cmonClient::prgindic 1
    set tb2  [$mainframe addtoolbar]
    set f3 [ frame $tb2.f3 ]
    label $f3.labf1 -text "Options: "  -relief flat
	pack  $f3.labf1 -anchor w -side left -padx 1 -pady 1
    cmonClient::createUserOptions $f3
	button $f2.sites -text "Update sites" -command cmonClient::maintainSites -fg blue
	button $f2.exit -text "Exit" -command "::handleExit" -fg white -bg red
	button $f2.help -text "Help" -command cmonClient::help -fg brown
	
	pack $passwdWidget $f2.globus $f2.sites $f2.exit -side left -padx 2 -pady 1
	pack $f2.help -side right -anchor e -padx 1 -pady 2
	pack $f1 -side left -anchor w
	pack $f2 -side right -padx 5 -anchor e
    pack $f3 -side left -anchor w
	set ::cmonClient::updatesitew $f2.sites
	
	set ::cmonClient::serverw [ $mainframe addindicator -text "cntlmonAPI ??" ]
    $mainframe addindicator -text "cmonClient $::version, pid [ pid ]"	
	$mainframe addindicator -text "BLT [ package version BLT ]"
    $mainframe addindicator -text "BWidget $::BWversion"
    $mainframe addindicator -text "Tk $tk_patchLevel"	
	
    ;## NoteBook creation
    set frame    [$mainframe getframe]
    set notebook [NoteBook $frame.nb]
	$notebook configure -bd 2 -foreground black -background gray \
	 -activeforeground white -activebackground blue
	set ::cmonClient::notebook $notebook
	
	set i 0
	foreach { page notice } $::cmonClient::pages {
		set ::cmonClient::prgtext  "Creating $notice page..."
		${page}::create $notebook
		incr ::cmonClient::prgindic
		incr i 1
	}
	
    set ::cmonClient::prgtext   "Done"
    $notebook compute_size
    pack $notebook -fill both -expand yes -padx 4 -pady 4
    $notebook raise [ $notebook page 1 ]
    pack $mainframe -fill both -expand yes
    update idletasks
    destroy .intro
}

§   §   §
cmonClient::state1 Description connected to server, has a socket id Parameters none Usage called connects to the selected server if not already connected otherwise nothing to do, it is connected.
Comments:
state and color is set when server acknowledges login and passwd.
proc cmonClient::state1 {} {
	if	{ $::USE_GLOBUS } {
    	return [ cmonGlobusChannel::state1 ]
    }
	if  { [ string equal $::cmonClient::var(site) $::cmonClient::NOSERVER ] } {
		set ack [ MessageDlg .cmonClientDlg -type ok -aspect 100 -justify left \
        	-message "Please select an LDAS site" -icon info \
    		-title Info -font $::MSGFONT ]
		return
	}
	if	{ [ string equal $::cmonClient::oldsite $::cmonClient::var(site) ] } {
		if	{ [ llength $::cmonClient::sid ] > 0 } {
			return
		}
	}
	set ::cmonClient::oldsite $::cmonClient::var(site)
	
	if  { [ llength $::cmonClient::sid ] == 0 } {
		if	{ [ catch {
			set login ""
			set passwd ""
			if	{ ! $::cmonClient::noPass } {
    			foreach { login passwd } [ validateLogin ] { break }
				if	{ ! [ string equal cancelled $login ] } {
					if	{ [ info exist ::siteport($::cmonClient::var(site)) ] } {
						foreach { host port } $::siteport($::cmonClient::var(site)) { break }
					} else {
						set host $::cmonClient::var(site)
						set port $::cntlmonPort
					}
    				set rc [ catch { set ::cmonClient::sid [ socket $host $port ] } err ]
					if	{ $rc } {
						error "Failed to connect to $host@$port: $err"
					}
                    if	{ ! [ regexp {control|monitor} $login ] } {
						set msg connect
					} else {
                        set msg "$login $passwd $::version $::cmonClient::serverKey"
                    }
    				fconfigure $::cmonClient::sid -buffering line
					puts $::cmonClient::sid $msg
    				fileevent $::cmonClient::sid readable \
   					[ list readData $::cmonClient::sid ]
					set ::cmonClient::state 1
					if	{ [ info exist ::cmonClient::reconnectId ] } {
						after cancel $::cmonClient::reconnectId
					}
                    after $::PINGDELAY cmonClient::pingServer
				}
			}
		} err ] } {
			puts $::logfd $err
			set ack [ MessageDlg .cmonClientDlg -type ok -aspect 100 -justify left \
        		-message "Connect failed: $err." -icon error \
    			-title Error -font $::MSGFONT ]
			cmonClient::setDisconnected
			return -code error $err
		}
	}
}

§   §   §
cmonClient::encryptUserPasswd Description encrypt a user's password with server sent key Parameters clear text passwd none Usage Comments:
assume server key has been received
proc cmonClient::encryptUserPasswd { passwd } {
    set passwd [ key::md5 $passwd ]
	if	{ ! [ string match nokey $::cmonClient::serverKey ] } {
		return [ key::md5 $passwd$::cmonClient::serverKey ]
	} else {
	    return $passwd
    }
}

§   §   §
cmonClient::sendUserPasswd Description encrypt a user's password with server sent key Parameters clear text passwd none Usage Comments:
assume server key has beenr received
proc cmonClient::sendUserPasswd { sid } {
	if	{ [ catch {
		set passwd [ encryptUserPasswd $::passwdtext ]
		puts $::cmonClient::sid "$::logintext $passwd $::version $::cmonClient::serverKey"
		flush $sid
	} err ] } {
		return -code error $err
	}
}

§   §   §
cmonClient::state0 Description transitions to state 0, the initial state.
Posssible triggers
  1. user exits or selects no-server
  2. lost connection to previous cntlmonAPI Parameters Usage Comments:
    Transition to this state0 depends on client confirmation or it could be forced due to lost of connection level : user, exit and lostconnect logfile is closed in handleExit
    proc cmonClient::state0 { { level user } { msg "" } } {
         if  	{ [ llength $::cmonClient::sid ] > 0 } {
    	 		;## no change if same site and connected
    	 		set flag 1
    			;## user wants to change sites but same site
             	if  { [ regexp {user} $level ] } {
    				if	{ ! [ string compare $::cmonClient::oldsite $::cmonClient::var(site) ] } {
    					return $::cmonClient::state
    				}
    			}	
    			;## user wants to change site or exit
    			switch -- $level {
    			    user -
    				exit { set choice [ showWarning "Do you really want to disconnect\n\
        			        from LDAS cntlmon API at $::cmonClient::oldsite?" okcancel ]
    						if  { $choice } {
                       			set ::cmonClient::var(site) $::cmonClient::oldsite
                       			set flag 0
                  			} elseif { [ string match exit $level ] } {
    							foreach pid $::cmonClient::childPids {
    								catch { exec kill -9 $pid }
    							}
                                cmonClient::setDisconnected 1
                                exitCleanup
                                clientExit
    					    }
    					}
    				lostconnect {
                    		set msg "Lost connection to LDAS cntlmon API at $::cmonClient::oldsite.\n\
        			        Please retry connection later."
                            set choice [ showWarning $msg ok ]
                            set flag 1
    						set ::cmonClient::var(site) $::cmonClient::NOSERVER
               			}
    				readerror {
    					catch { close $::cmonClient::sid } err
    					set flag 1
    					set errmsg "$msg, disconnected"
    					set choice [ MessageDlg .cmonClientDlg -type ok -aspect 120 \
           				-message $errmsg -icon error -title Error -justify left -font $::MSGFONT ]
    					}
    			}
               	if  	{ $flag } {
    					cmonClient::setDisconnected 1
                }
    	} else {
    		set ::cmonClient::state 0
    	}
    	if	{ ! $::cmonClient::state } {
    		cmonClient::enableSites
    	}	
    	return $::cmonClient::state
    }
    
    
    §   §   §
    cmonClient::state01 Description transitions to state 0, the initial state.
    and then to state1 if conditions permit (user confirmed)
    a site must be chosen here Parameters Usage Comments:
    proc cmonClient::state01 { { site "" } } {
    	;## the following transitions by disconnecting from one site and
    	;## connecting to the next after the user's consent.	
    	if	{ [ catch {
    		set seqpt "state0"
    		set state [ state0 ]
    		if	{ ! $state } {
    			set seqpt "state1"
    			set ::cmonClient::var(site) $site
    			state1
    		}
    	} err ] } {
    		 #puts "error: $seqpt $err"
    		 cmonClient::state0 lostconnect
    	}
    }
    
    
    §   §   §
    cmonClient::setConnected Description set the connected light after login & passwd are accepted Parameters Usage Comments:
    proc cmonClient::setConnected {} {
    	foreach { host port } $::siteport($::cmonClient::var(site)) { break }
    	$::cmonClient::connectw configure -text \
       		[ format %-40.40s "$::cmonClient::client@$host" ] -bg green
    	set ::cmonClient::state 1
    	$::cmonClient::updatesitew configure -state disabled
        set globusButton [ set ::cmonClient::globusToggle ]
        $globusButton configure -state disabled
    	$::cmonClient::serverw configure -text "cntlmonAPI $::cmonClient::serverVersion"
        ;## label each package for reset but dont do it until page is raised
    	eval $::cmonClient::pageUpdate
        cmonClient::sendNoOp
    }
    
    
    §   §   §
    cmonClient::setDisconnected Description set things that are for disconnection Parameters Usage Comments:
    proc cmonClient::setDisconnected { { reset 0 } } {
    	set ::cmonClient::state 0	
        set site $::cmonClient::var(site)
        if  { ! [ string match $::cmonClient::NOSERVER $site ] } {
            catch { unset ::beowulfNodes($site) }
        }
    	set ::cmonClient::var(site) $::cmonClient::NOSERVER
        catch { close $::cmonClient::sid } err
        set globusButton [ set ::cmonClient::globusToggle ]
        $globusButton configure -state normal
        set ::cmonClient::sid {}
    	set ::cmonClient::client new
    	set ::cmonClient::state 0
       	$::cmonClient::connectw configure -text $::cmonClient::notConnected -bg orange
        ;## trigger trace
    	set ::beowulfNodes($::cmonClient::NOSERVER) \
    		[ set ::beowulfNodes($::cmonClient::NOSERVER) ]
    	
        ;## remove some server vars	
    	$::cmonClient::updatesitew configure -state normal
    	$::cmonClient::serverw configure -text "cntlmonAPI ??"
    	set ::cmonClient::serverKey nokey
    	eval $::cmonClient::pageUpdate
        ;## disable ping for globus
    	catch { after cancel $::cmonClient::NoOpId }
        catch { unset ::cmonClient::NoOpId }
    }
    
    
    §   §   §
    cmonClient::retryConnect Description retry connection Parameters Usage Comments:
    proc cmonClient::retryConnect { site { delay 50000 } { retry 0 } } {
    	
    	if	{ $::cmonClient::state == 1 } {
    		# cmonClient::enableSites
    		return
    	}
    	if	{ [ catch {
    		set ::cmonClient::noPass 0
    		cmonClient::disableSites $site
    		cmonClient::state1
    		catch { unset ::cmonClient::reconnectId }
    		if	{ $::cmonClient::state != 1 } {
    			incr retry 1
    			if	{ $retry < $::RECONNECT_TIMES } {
    				set ::cmonClient::reconnectId \
    				[ after $delay cmonClient::retryConnect $delay $retry ]
    			}
    		}
    	} err ] } {
    		cmonClient::state0 lostconnect
    	}
    	cmonClient::enableSites	
    }
    
    
    §   §   §
    cmonClient::disableSites Description retry connection Parameters Usage Comments:
    proc cmonClient::disableSites { site } {
    	set sitemenu $::cmonClient::sitemenu
    	for { set i 0 } { $i < [ llength $::cmonClient::sites ] } { incr i 1 } {
            $sitemenu entryconfigure $i -state disabled
        }
    	set ::cmonClient::var(site) $site
    }
    
    
    §   §   §
    cmonClient::enableSites Description retry connection Parameters Usage Comments:
    proc cmonClient::enableSites {} {
    	set sitemenu $::cmonClient::sitemenu
    	for { set i 0 } { $i < [ llength $::cmonClient::sites ] } { incr i 1 } {
            $sitemenu entryconfigure $i -state normal
        }
    }
    
    
    §   §   §
    cmonClient::maintainSites Description transitions to state 0, the initial state.
    and then to state1 if conditions permit (user confirmed)
    a site must be chosen here Parameters Usage Comments:
    proc cmonClient::maintainSites {} {
    	if	{ $::cmonClient::state != 0 } {
    		set msg "Please disconnect from $::cmonClient::var(site) first"
    		set choice [ MessageDlg .cmonClientDlg -type ok -aspect 120 \
           				-message $msg -icon error -title Error -justify left -font $::MSGFONT ]
    		return
    	}
    	if	{ [ winfo exist $::cmonClient::whostName ] } {
    		return
    	}
    	if	{ [ catch {
    		catch { unset ::cmonClient::sitedialog }
    		set dlg [Dialog $::cmonClient::whostName -parent . -modal local \
              	-separator 1 -title   "cntlmon API server site: " \
            	-side bottom -default 0 -cancel 3 ]
       	 	set badd [$dlg add -name 0 -command "cmonClient::addSite $dlg" -text add -state disabled ]
    		set bupdate [$dlg add -name 1 -command "cmonClient::updateSite $dlg" -text update -state disabled ]
    		set bdel [ $dlg add -name 2 -command "cmonClient::deleteSite $dlg" -text delete -state disabled ]
        	$dlg add -name cancel -command "destroy $dlg"
        	set dlgframe [$dlg getframe]
    		
    		set f1 [ frame $dlg.fsite ]
    		set lab [ label $f1.lbl -text "              Site domain:" -width 20 -justify right ]
    		set othersite [ComboBox $f1.site  -width 5 \
                       -textvariable ::cmonClient::sitedialog\(othersite\) \
                       -values [ concat other $::cmonClient::sites ] \
                       -helptext "Your site's domain name e.g. ldas-dev"   \
    				   -editable no \
                       -modifycmd "cmonClient::displaySite $dlg $f1.site $badd $bupdate $bdel" ]
    		pack $lab -side left -anchor e
    		pack $othersite -side right -fill x -expand 1
    		pack $f1 -side top -fill x -expand 1
    		set ::cmonClient::sitedialog(othersite) [ lindex $::cmonClient::sites 0 ]
    		set ::cmonClient::sitedialog(otheraddr) ""
    		set otheraddr  [LabelEntry $dlg.addr -label "Site address: " -labelwidth 20 -labelanchor e \
                       -textvariable ::cmonClient::sitedialog(otheraddr) -editable 1 -width 20 -labeljustify right \
                       -helptext "Your site's full address e.g. ldas-dev.ligo.caltech.edu or 131.215.115.248" ]
    		set ::cmonClient::sitedialog(otherport) ""
    		set otherport  [LabelEntry $dlg.port -label "cntlmon API port: " -labelwidth 20 -labelanchor e \
                       -textvariable ::cmonClient::sitedialog(otherport) -editable 1 -width 10 -labeljustify right \
                       -helptext "The port number of the cntlmonAPI for this site, e.g. 10000" ]
            set ::cmonClient::sitedialog(otherport) 10000
    		set ::cmonClient::sitedialog(otherdbnames) ""
    		set otherdbnames  [LabelEntry $dlg.dbnames -label "Site databases: " -labelwidth 20 -labelanchor e \
                       -textvariable ::cmonClient::sitedialog(otherdbnames) -editable 1 -width 20 -labeljustify right \
                       -helptext "(Optional) databases at your site, e.g. cit_test cit_1" ]
    				
    		$othersite bind <Return> "focus $otheraddr"
    		$otheraddr bind <Return> "focus $otherport"
    		$otherport bind <Return> "focus $otherdbnames"
    		$otherdbnames bind <Return> "focus $otheraddr"
    		
    		pack $othersite $otheraddr $otherport $otherdbnames -side top -fill x -expand 1
    		$dlg draw
    		# destroy $dlg
    	} err ] } {
    		set ack [ MessageDlg .cmonClientDlg -type ok -aspect 120 \
    			-message $err -icon error ]
    		catch { destroy $dlg }
    	}
    }
    
    
    §   §   §
    cmonClient::displaySite Description display site info Parameters Usage Comments:
    proc cmonClient::displaySite { dialog sitew badd bupdate bdel } {
    	setCombo $sitew
    	set ::cmonClient::sitedialog(otherdbnames) ""
    	set ::cmonClient::sitedialog(otheraddr) ""
    	set ::cmonClient::sitedialog(otherdbnames) ""
    	if	{ ! [ regexp {^(no-server|other)$} $::cmonClient::sitedialog(othersite) ] } {
    		foreach { host port } $::siteport($::cmonClient::sitedialog(othersite)) { break }
    		set ::cmonClient::sitedialog(otheraddr) $host
    		set ::cmonClient::sitedialog(otherport) $port
    		catch { set ::cmonClient::sitedialog(otherdbnames) $::dbnames($::cmonClient::sitedialog(othersite)) }
    	}
    	if	{ [ string match other $::cmonClient::sitedialog(othersite) ] } {
    		$sitew configure -editable yes
    		$badd configure -state normal
    		$bupdate configure -state disabled
    		$bdel configure -state disabled
    	} else {
    		$sitew configure -editable no
    		$badd configure -state disabled
    		$bupdate configure -state normal
    		$bdel configure -state normal
    	}
    }
    
    
    §   §   §
    cmonClient::addSite Description add a new server site Parameters Usage Comments:
    proc cmonClient::addSite { dialog } {
    	
    	set text ""
    	set siteindex [ lsearch -exact $::cmonClient::sites $::cmonClient::sitedialog(othersite) ]
    	if	{ $siteindex > -1 } {
    		set ack [ MessageDlg .cmonClientDlg -type ok -aspect 120 \
    			-message "$::cmonClient::sitedialog(othersite) already exists." \
    			-icon error -justify left -font $::MSGFONT ]
    		return
    	}
    	if	{ [ string equal other $::cmonClient::sitedialog(othersite) ] } {
    		set ack [ MessageDlg .cmonClientDlg -type ok -aspect 120 \
    			-message "Other cannot be used for site name" \
    			-icon error -justify left -font $::MSGFONT ]
    		return
    	}
    	if	{ ! [ string length $::cmonClient::sitedialog(otheraddr) ] } {
    		set ack [ MessageDlg .cmonClientDlg -type ok -aspect 120 \
    			-message "Please enter a site address or IP address" \
    			-icon error -justify left -font $::MSGFONT ]
    		return
    	}
    	if	{ ! [ string length $::cmonClient::sitedialog(otherport) ] } {
    		set ack [ MessageDlg .cmonClientDlg -type ok -aspect 120 \
    			-message "Please enter your cntlmon API server port" \
    			-icon error -justify left -font $::MSGFONT ]
    		return
    	}
    	if	{ ! [ regexp {^\d+} $::cmonClient::sitedialog(otherport) ] } {
    		set ack [ MessageDlg .cmonClientDlg -type ok -aspect 120 \
    			-message "cntlmon API server port must be numeric" \
    			-icon error -justify left -font $::MSGFONT ]
    		return
    	}
    	if	{ [ string length $::cmonClient::sitedialog(otherdbnames) ] } {
    		foreach db $::cmonClient::sitedialog(otherdbnames) {
    		   if	{ [ string length $db ] > 8 } {
    		   		set ack [ MessageDlg .cmonClientDlg -type ok -aspect 120 \
    				-message "Database name $db exceeds 8 characters" \
    				-icon error -justify left -font $::MSGFONT ]
    				return
    			}
    		}
    		array set ::dbnames [ list $::cmonClient::sitedialog(othersite) \
    			$::cmonClient::sitedialog(otherdbnames) ]
    		append text "array set ::dbnames \[ list $::cmonClient::sitedialog(othersite) \
    			$::cmonClient::sitedialog(otherdbnames) \]\n"
    	} else {
    		array set ::dbnames [ list $::cmonClient::sitedialog(othersite) { none } ]
    		append text "array set ::dbnames \[ list $::cmonClient::sitedialog(othersite) \{ none \} \]\n"
    	}
    	array set ::LDASmachines [ list $::cmonClient::sitedialog(othersite) \
    		{ gateway dataserver metaserver datacon controlmon beowulf }  ]
    	append text "array set ::LDASmachines \[ list $::cmonClient::sitedialog(othersite) \
    		\{ gateway dataserver metaserver datacon controlmon beowulf \}  \]\n"
    	array set ::siteport [ list $::cmonClient::sitedialog(othersite) \
    		[ list $::cmonClient::sitedialog(otheraddr) $::cmonClient::sitedialog(otherport) ] ]
    	append text "array set ::siteport \[ list $::cmonClient::sitedialog(othersite) \
    		\[ list $::cmonClient::sitedialog(otheraddr) $::cmonClient::sitedialog(otherport) \] \]\n"
    	if	{ $siteindex < 0 } {
    		set ::cmonClient::sites [ linsert $::cmonClient::sites end-1 $::cmonClient::sitedialog(othersite) ]
    	}
    	set fd [ open $::statefile a+ ]
    	set text [ string trim $text \n ]
    	puts $fd $text 	
    	close $fd
    	destroy $dialog
    	set ack [ MessageDlg .cmonClientDlg -type ok \
    		-message "$::cmonClient::sitedialog(othersite) added to server sites and saved in $::statefile." -icon info ]
    	;## add site to options menu
    	cmonClient::createSiteOptions
    	#set ::cmonClient::var(site) $::cmonClient::sitedialog(othersite)
    	catch { unset ::cmonClient::sitedialog }
    }
    
    
    §   §   §
    cmonClient::createSiteOptions Description create the options menu when sites are new or other sites have been added.
    Usage Comments:
    The sites are sorted when recreated.
    proc cmonClient::createSiteOptions { { sitew "" } } {
    	if	{ [ string length $sitew ] } {
        	set ::cmonClient::sitew $sitew
        }
        catch { destroy $::cmonClient::sitew }
        set sites [ lsort -ascii [ array names ::siteport ] ]
        set ::cmonClient::sites [ concat no-server $sites ]
    	set sitemenu [ eval tk_optionMenu $::cmonClient::sitew \
    		::cmonClient::var(site) $::cmonClient::sites ]
        for { set i 0 } { $i < [ llength $::cmonClient::sites ] } { incr i 1 } {
    		set site [ lindex $::cmonClient::sites $i ]
    		if	{ [ string match $::cmonClient::NOSERVER $site ] } {
    			$sitemenu entryconfigure $i -command  cmonClient::state0
    		} else {
            	$sitemenu entryconfigure $i -command "cmonClient::state01 $site"
    		}
        }
    	set ::cmonClient::sitemenu $sitemenu
    	catch { destroy $::cmonClient::connectw }
    	set parent [ winfo parent $::cmonClient::sitew ]
    	label $parent.connects -text $::cmonClient::notConnected -relief ridge -bg orange \
            -borderwidth 2
        set ::cmonClient::connectw $parent.connects
        pack $::cmonClient::sitew $parent.connects -side left -fill y -padx 1 -anchor w
    	set ::cmonClient::var(site) $::cmonClient::NOSERVER
    	
    }
    
    
    §   §   §
    cmonClient::createUserOptions Description create the options menu when sites are new or other sites have been added.
    Usage Comments:
    The sites are sorted when recreated.
    proc cmonClient::createUserOptions { parent } {
    	if	{ [ catch {
        	set but1 [ checkbutton $parent.info -text "show info" -anchor w \
            	-variable ::POPUP_SHOW_INFO \
            	-font $::LISTFONT -onvalue 1 -offvalue 0 -relief sunken -borderwidth 2 ]
    		if	{ ! [ info exist ::POPUP_SHOW_INFO ] } {
            	set ::POPUP_SHOW_INFO 1
            }
            set but2 [ checkbutton $parent.warn -text "show warnings"  -anchor w \
            	-variable ::POPUP_SHOW_WARN \
            	-font $::LISTFONT -onvalue 1 -offvalue 0 -relief sunken -borderwidth 2 ]
            if	{ ! [ info exist ::POPUP_SHOW_INFO ] } {
    			set ::POPUP_SHOW_WARN 1
            }
            set but3 [ checkbutton $parent.printqueue -text "print queue" -anchor w \
            	-variable ::POPUP_PRINT_QUEUE -onvalue 1 -offvalue 0 -borderwidth 2 \
            	-font $::LISTFONT -command blt_graph::showPrintQueue -relief sunken ]
            set ::POPUP_PRINT_QUEUE 0
            pack $but1 $but2 $but3 -side left -padx 2
     	} err ] } {
        	return -code error $err
        }
    }
    
    
    §   §   §
    cmonClient::updateSite Description removes a site .e.g. which has been added by other Usage Comments:
    proc cmonClient::updateSite { dialog } {
    	set text ""
    	set site $::cmonClient::sitedialog(othersite)
    	array set ::siteport [ list $::cmonClient::sitedialog(othersite) \
    		[ list $::cmonClient::sitedialog(otheraddr) $::cmonClient::sitedialog(otherport) ] ]
    	append text "array set ::siteport \[ list $::cmonClient::sitedialog(othersite) \
    		\[ list $::cmonClient::sitedialog(otheraddr) $::cmonClient::sitedialog(otherport) \] \]\n"
    	set pattern "(siteport|dbnames).+$site "
    	if	{ [ string length  $::cmonClient::sitedialog(otherdbnames) ] } {
    		array set ::dbnames [ list $::cmonClient::sitedialog(othersite) \
    		$::cmonClient::sitedialog(otherdbnames) ]
    		append text "array set ::dbnames \[ list $::cmonClient::sitedialog(othersite) \
    			\[ list $::cmonClient::sitedialog(otherdbnames) \] \]\n"
    	} else {
    		array set ::dbnames [ list $::cmonClient::sitedialog(othersite) none ]
    		append text "array set ::dbnames \[ list $::cmonClient::sitedialog(othersite) \{ none \} \]\n"
    	}
    	set text [ string trim $text \n ]
    	set newtext ""
    	if	{ [ file exist $::statefile ] } {
    		set fd [ open $::statefile r ]
    		set data [ read -nonewline $fd ]
    		close $fd
    		set data [ split $data \n ]
    		set site $::cmonClient::sitedialog(othersite)
    		foreach line $data {
    			if	{ ! [ regexp $pattern $line ] } {
    				append newtext $line\n
    			}
    		}
    		append newtext $text
    		set newtext [ string trim $newtext \n ]
    		set fd [ open $::statefile w ]
    		;## open for writing
    		puts $fd $newtext 	
    		close $fd
    	}
    	destroy $dialog
    	set ack [ MessageDlg .cmonClientDlg -type ok \
    		-message "Updated information for site $::cmonClient::sitedialog(othersite) and\n\
    		saved in $::statefile" -icon info ]
    	catch { unset ::cmonClient::sitedialog }
    }
    
    
    §   §   §
    cmonClient::deleteSite Description removes a site .e.g. which has been added by other Usage Comments:
    proc cmonClient::deleteSite { dialog } {
    	if	{ ! [ string match no-server $::cmonClient::sitedialog(othersite) ] } {
    		set ack [ MessageDlg .cmonClientDlg -type okcancel -aspect 120 \
    			-message "Do you really want to delete $::cmonClient::sitedialog(othersite) " \
    			-icon question -justify left -font $::MSGFONT ]
    		if	{ [ string match cancel $ack ] } {
    			return
    		}
    	}
        set site $::cmonClient::sitedialog(othersite)
        set siteindex [ lsearch -exact $::cmonClient::sites $::cmonClient::sitedialog(othersite) ]
    	set ::cmonClient::sites [ lreplace $::cmonClient::sites $siteindex $siteindex ]
        unset ::siteport($site)
        catch { unset ::dbnames($site) }
        unset ::LDASmachines($site)
    	set pattern "$::cmonClient::sitedialog(othersite) "
    	destroy $dialog
    	set newtext ""
    	if	{ [ file exist $::statefile ] } {
    		set fd [ open $::statefile r ]
    		set data [ read -nonewline $fd ]
    		set data [ split $data \n ]
    		set newtext ""
    		foreach line $data {
    			if	{ ! [ regexp $pattern $line ] } {
    				append newtext $line\n
    			}
    		}
    		set newtext [ string trim $newtext \n ]
    		set fd [ open $::statefile w ]
    		;## open for writing
    		puts $fd $newtext
    		close $fd 	
    	}
    	cmonClient::createSiteOptions
    	set ack [ MessageDlg .cmonClientDlg -type ok \
    		-message "$::cmonClient::sitedialog(othersite) deleted" -icon info ]
    	catch { unset ::cmonClient::sitedialog }
    }
    
    
    §   §   §
    ##*******************************************************
    Name: cmonClient::helpTips

    Description:

    Parameters:
    Usage:
    
    Comments:
    
    proc cmonClient::help {} {
    	set msg "Please visit LDAS home page, link 'using ControlmonAPI Client' for details"
        catch { set ack [ MessageDlg .filtertips -title Tips -type ok -aspect 300 \
    		-message $msg -icon info -justify left -font $::MSGFONT ] }
    		
    }
    
    
    §   §   §
    ##*******************************************************
    Name: cmonClient::useGlobusToggle

    Description:

    Parameters:
    Usage:
    
    Comments:
    
    proc cmonClient::useGlobusToggle { button } {
    	if	{ $::POPUP_SHOW_INFO } {
    		set msg "To change this option again when connected to a server, you would need to disconnect, \
        	 select this option and reconnect your cmonClient to the server. If you get an error with Globus, \
             you will need to use cmonClient 1.8.0 for servers that are still on raw GSI sockets."
            set icon info
        	set image [ Bitmap::get info ]
            set dname .cmonClientGlobusToggle
            if	{ ![ winfo exist $dname ] } {
       			set dlg [ Dialog .cmonClientGlobusToggle -parent . -modal none \
              		-separator 1 -title  $icon \
            		-side bottom -anchor  s -default 0 -image $image ]
                $dlg add -name ok -anchor s -command [ list destroy $dlg ]
    		    set dlgframe [ $dlg getframe ]
    		    message $dlgframe.msg -aspect 300 -text $msg \
    					-justify left -font $::MSGFONT
    		    pack $dlgframe.msg -side top -anchor w
    		    $dlg draw
    			after 15000 [ list destroy $dlg ]
           	}
        }
        if	{ [ catch {
        	;## EXPIRE USER NAME AND PASSWORD IF ANY
            foreach var [ list cntllogin cntlpasswd ] {
                if  { [ info exist ::$var ] } {
                    unset ::$var
                }
            }
        	if	{ $::USE_GLOBUS } {
            	cmonGlobusChannel::init
            }
        } err ] } {
    		catch { set ack [ MessageDlg .errorDialog[clock seconds ] -title Tips -type ok -aspect 300 \
    			-message $err -icon error -justify left -font $::MSGFONT ] }
    	}
    }
    
    
    §   §   §
    ##*******************************************************
    Name: cmonClient::helpTips

    Description:

    Parameters:
    Usage:
    
    Comments:
    
    does not work if cntlmonAPI no longer use tcl socket for operator port
    proc cmonClient::pingServer {} {
        if  { $::cmonClient::state != 1 } {
            after $::PINGDELAY cmonClient::pingServer
            return
        }
        if  { [ catch {
            foreach { host port } $::siteport($::cmonClient::var(site)) { break }
            set sid [ socket $host $port ]
            close $sid
        } err ] } {
        	set msg "[myName]: $err"
        	if	{ [ info exist ::logfd ] } {
            	catch { puts $::logfd $msg ; flush $::logfd }
            } else {
            	puts $msg
            }
            catch { close $sid }
            cmonClient::state0 lostconnect
        }
        after $::PINGDELAY cmonClient::pingServer
    }
    
    
    §   §   §
    ##*******************************************************
    Name: cmonClient::init

    Description:
    set up vars if they dont exist
    Parameters:
    Usage:
    
    Comments:
    
    proc cmonClient::init {} {
        ;## set up xterm
        set ::XTERM  [ auto_execok xterm ]
        if	{ ! [ string length $::XTERM ] } {
    	    if 	{ [ string match $::tcl_platform(os) SunOS ] } {
    		    set ::XTERM /usr/openwin/bin/xterm
    	    } else {
    		    set ::XTERM /usr/X11R6/bin/xterm
    	    }
        }
        if	{ ! [ file exist $::XTERM ] } {
    	    set ack [ tk_messageBox -type ok -default ok \
    		    -message "Please define xterm path in cmonClient resource" ]
        }
        if  { ! [ info exist ::SERVER_DELAY ] } {
            set ::SERVER_DELAY 3600000
        }
        if  { ! [ info exist ::PINGDELAY ] } {
            set ::PINGDELAY 900000
        }
        ;## max time out for passwd is set to 30 min
        if  { ! [ info exist ::LOCKTIMEOUT] } {
            set  ::LOCKTIMEOUT 30
        }
        ;## update password widget every 1 min
        if  { ! [ info exist ::LOCK_INTERVAL ] } {
            set  ::LOCK_INTERVAL 60000
        } else {
            set ::LOCK_INTERVAL [ expr $::LOCK_INTERVAL * 60000 ]
        }
        if	{ ! [ info exist ::PRINTCMD ] } {
    	    set ::PRINTCMD lpr
        }
        if	{ ! [ info exist ::SERVICE_NAME ] } {
           	set ::SERVICE_NAME ldas
        }
    }
    
    
    §   §   §

    Name: cmonClient::saveState

    Description:
    save state info Usage:
    
    Comments:
    
    proc cmonClient::saveState {} {
    	if	{ [ file exist $::statefile ] } {
        	set fd [ open $::statefile r ]
            set data [ read -nonewline $fd ]
            close $fd
            foreach var [ list POPUP_SHOW_INFO POPUP_SHOW_WARN RSC_LAST_DIFF_VERSION ] {
                if  { ! [ info exist ::$var ] } {
                    continue
                }
            	set rc [ regsub -- "set ::$var \\S+" $data "set ::$var [ set ::$var ]" x ]
            	if	{ !$rc } {
            		append data "\nset ::$var [ set ::$var ]"
            	} else {
                	set data $x
                }
            }
            set fd [ open ${::statefile}.tmp w ]
    		puts $fd [ string trim $data \n ]
            close $fd
            file rename -force ${::statefile}.tmp $::statefile
        }
    }
    
    
    §   §   §

    Name: cmonClient::NoOpReply

    Description:
    save state info Usage:
    
    Comments:
    
    NoOp callback
    proc cmonClient::NoOpReply { page rc clientCmd html } {
    	set name cmonClient
        set clientCmd [ split $clientCmd : ]
        set client [ lindex $clientCmd 0 ]
        set cmdId [ lindex $clientCmd 1 ]
        set afterid [ lindex $clientCmd 2 ]
        # puts "in NoOpReply, rc=$rc, page=$page, client=$client,cmdId=$cmdId, afterid=$afterid"
        # set ::cmonClient::client $client
        ;## turn ping off if server does not support it
        if	{ [ string match "*invalid command*cntlmon::NoOp*" $html ] } {
        	catch { after cancel $::cmonClient::NoOpId }
            set ::cmonClient::NoOpId stop
            return
        }
        ;## turn ping off also if error
        if	{ ! [ string match "*NoOp*" $html ] } {
    		set ack [ tk_messageBox -type ok -default ok \
    			-message $html \
    				-icon info ]
            catch { after cancel $::cmonClient::NoOpId }
            set ::cmonClient::NoOpId stop
        }
    }
    
    
    §   §   §

    Name: cmonClient::sendNoOp

    Description:
    save state info Usage:
    
    Comments:
    
    send no op only if there is outstanding request silent if there are errors as the server could be disconnected
    proc cmonClient::sendNoOp {} {
    	if	{ $::cmonClient::state } {
        	if	{ $::USE_GLOBUS } {
    			set cmdId new
        		set client $::cmonClient::client
        		set repeat 0
        		set freq 0
            	set page cmonClient
    			if	{ [ catch {
    				set cmd "cmonClient::NoOpReply $page\n$repeat\n$freq\n\$client:$cmdId\ncntlmon::NoOp"
    				sendCmd $cmd
    			} err ] } {
    				#set ack [ tk_messageBox -type ok -default ok \
    				#-message "sendNoOp error: $err" \
    				#-icon error ]
                    set ::cmonClient::NoOpId stop
    			}
                ;## if server does not support this dont send it
                if	{ [ info exist ::cmonClient::NoOpId ] } {
                	if	{ [ string equal stop $::cmonClient::NoOpId ] } {
                        unset ::cmonClient::NoOpId
                    	return
                    }
                }
                set ::cmonClient::NoOpId [ after 5000 cmonClient::sendNoOp ]
           	} else