# $Id: remote_ctl.tcl 1208 2007-10-24 19:34:38Z oliver $ # based on bounce.tcl and vmdcollab.tcl # from http://www.ks.uiuc.edu/Research/vmd/script_library/scripts/vmdcollab/ # with help from Justin Gullingsrud # # Start this script in VMD and send commands to the listening port to have VMD # execute them remotely (eg 'telnet localhost 5555'). However, this is really # meant to be used with vmd_ctl.py. That is why we have a special # RESPONSE_TERMINATOR to ease the communication (see Protocol) # # Usage: vmd -e remote_ctl.tcl # or vmd> source remote_ctl.tcl # # Security: we only allow connections from localhost (see acpt) # # Protocol: port 5555 # RESPONSE_TERMINATOR to signify end of VMDs response # # Commands: some transmitted commands have special meaning and are interpreted # by the server and not passed on to Tcl: # # close close the current connection (socket) # exit exit the server (no more connections possible, # but current connections are still open) # loglevel N set LOGLEVEL to value N (0<=N<=3) namespace eval remote_ctl { variable main variable clients variable default_vmd_port 5555 variable RESPONSE_TERMINATOR "__END_OF_VMD_RESPONSE__\n" # important: puts translates '\n' -> '\r\n' (which MUST be used in vmd_ctl.py) variable LOGLEVEL 1 # loglevel: 0 quiet # 1 startup and quit message, unauthorised connections # 2 socket closures # 3 commands proc start "{port $default_vmd_port}" { variable main set main [socket -server remote_ctl::acpt $port] putlog "Listening on port $port" 1 } proc acpt { sock addr port } { variable clients if {[string compare $addr "127.0.0.1"] != 0} { putlog "Unauthorized connection attempt from $addr port $port" 1 close $sock return } putlog "Accept $sock from $addr port $port" 2 set clients($sock) 1 fconfigure $sock -buffering line fileevent $sock readable [list remote_ctl::recv $sock] } proc recv { sock } { variable main variable clients variable RESPONSE_TERMINATOR variable LOGLEVEL if { [eof $sock] || [catch {gets $sock line}] || \ [string compare $line "close"] == 0} { # end of file, abnormal connection drop, or requested: # shut down this connection close $sock putlog "Closing $sock" 2 unset clients($sock) } else { if {[string compare $line "exit"] == 0} { # prevent new connections # existing connections stay open putlog "Disallowing further incoming connections by request of $sock" 1 close $main return } elseif {[regexp {loglevel *([0-9])} $line dummy LOGLEVEL]} { putlog "Changed loglevel to $LOGLEVEL" 1 return } putlog "running line: $line" 3 # execute the received commands and send back result set rc [catch $line result] if { $rc } { puts $sock "Error executing command '$line': \n$result" puts "Error executing command '$line': \n$result" } else { puts $sock $result puts $result } # terminator -- must correspond to RESPONSE_TERMINATOR in vmd_ctl.py puts -nonewline $sock $RESPONSE_TERMINATOR } } proc putlog { text {threshold 0} } { variable LOGLEVEL if {[expr $LOGLEVEL >= $threshold]} { puts $text } return } } # assuming that loading this file means that we actually want to run # the server, we just start it: remote_ctl::putlog "Starting remote_ctl server in VMD." if {[info exists ::env(VMD_REMOTE_PORT)]} { remote_ctl::start $::env(VMD_REMOTE_PORT) } else { remote_ctl::start }