Remote execution software is translated into ''Logiciel d`exécution à distance'' in French. It is what ''distanciel (distance logiciel)'' means to be. ''20060606 [Sarnold]'' There are 4 files needed : 3 on the 'client', only one on the 'server'. Architecture is a bit like [X Window] model. * The 'server' is the machine on which the user interacts with a GUI. It needs [wish]. * The 'client' is the machine where the software really turns. It only needs [tclsh]. The 'server' opens a TCP port 2006, waiting for 'client' requests. Currently, ''distanciel'' is just a prototype, or fun project if you like more, but it works for simple tasks. Update to support BWidgets - ''20060607 Sarnold'' ---- '''Client File #1 : distanciel.tcl''' namespace eval distanciel { variable server localhost variable port 2006 variable socket variable delayed variable limit 10 proc connect {args} { variable server variable port variable socket if {[catch {set socket [socket $server $port]}]} { error "cannot connect to server $server on port $port" } return } proc send {args} { connect variable socket # write ,data set data [string length $args]\n$args for {set i 0} {$i < 3 && [set b [catch {puts -nonewline $socket $data} msg]]} {incr i} { # do nothing } catch {flush $socket} if {$b} { error "cannot write after $i attempts" } # read ,data gets $socket count foreach {script data} [read $socket $count] {break} close $socket # todo : variable handling uplevel $script return $data } proc delete {cmd op} { send delegate $cmd } proc renproc {oldname newname op} { send delegate [list rename $oldname $newname] } } package provide distanciel 0.1 ---- '''Client file #2 : distclt.tcl''' source distanciel.tcl foreach name {button label frame} { set body { # creates the widget distanciel::send create [linsert $args 0 NAME $obj] # instanciates a wrapper to handle widget methods proc $obj {args} [string map [list OBJ $obj] { distanciel::send action [info level 0] }] trace add command $obj rename distanciel::renproc # next line causes severe errors #trace add command $obj delete distanciel::renproc return $obj } proc $name {obj args} [string map [list NAME $name] $body] } foreach name { pack place grid winfo wm destroy bind tk option bindtags } { proc $name {args} [string map [list NAME $name] { distanciel::send delegate [linsert $args 0 NAME] }] } trace add execution exit enter distanciel::delete # some hacks # proc dputs {args} {puts $args} # because we emulate Tk, and because some megawidgets call "package require Tk" package provide Tk [package require Tcl] set argv0 [lindex $argv 0] set argv [lrange $argv 1 end] source $argv0 while {1} { update after 500 distanciel::send script } ---- '''Server : distserver.tcl''' package require Tk proc dputs {arg} { set fd [open traces.txt a] puts $fd $arg close $fd } file delete traces.txt set script "" proc recv {channel args} { #fconfigure $channel -translation binary gets $channel count if {[catch {set data [read $channel $count]}]} {return} set types {action create delegate} if {[llength $data]>2} { dputs $data error "internal error" } dputs $data foreach {type cmd} $data {break} set quit no switch -- $type { action - create - delegate - script {set data [eval [linsert $cmd 0 $type]]} default {error "unknown request type, should be one of : $types"} } #dputs Resp:$data puts -nonewline $channel [string length $data]\n$data flush $channel close $channel if {$quit} { after 1000; # here to flush network buffers before exiting exit } } proc action {args} { seceval $args } # secure eval proc seceval {args} { if {[catch {eval [lindex $args 0]} msg]} { return [list [list error $msg] ""] } list "" $msg } rename exit __exit__ proc exit {{code 0}} { callback [list exit $code] after 1000 __exit__ } proc delegate {args} { switch -- [lindex $args 0] { exit { # delay exit script uplevel set quit yes return [list $args ""] } bind { # the third argument to the bind command is a callback lset args 3 [list callback [lindex $args 3]] } default { # nothing to be done } } seceval $args } proc create {args} { foreach {cmd obj} [set l [lrange $args 0 1]] {break} foreach {opt value} [lrange $args 2 end] { switch -- $opt { -command {lappend l $opt [list callback $value]} default {lappend l $opt $value} } } list "" [eval $l] } proc callback {arg} { append ::script $arg\n } proc script {args} { set s $::script set ::script "" return [list $s ""] } socket -server recv 2006 ---- '''Client file #3 : the test program (mytest.tcl)''' label .lbl -text "Hello Stéphane!" button .b -command exit -text Exit pack .lbl .b or even, with [BWidget] : package require BWidget Label .lbl -text "Hello Stéphane!" Button .b -command exit -text Exit pack .lbl .b ''How to launch it :'' # on the server wish distserver.tcl & # on the client tclsh distclt.tcl mytest.tcl & ---- See also [Remote Script Execution]. ---- [[ [Category Networking] | [Category Interprocess Communication] ]]