Version 2 of GIN

Updated 2006-01-11 09:16:18

GIN was inspired by tequila from jcfontain

but i wanted a distributed system with no dedicated server and faster performance.

gin uses udp packets from either

  • Tnm by Juergen Schoenwaelder <[email protected]> this is currently broken as Tnm Udp Stuff seems to have been turned inside out.

or

  • TclUDP from Pat Thoyts

Multicast Adresses are used for sending to multiple addresses.

In my application sensors of any kind send their data on a multicast channel. Ackknowledges of commands ( to any actor ) are repeated on a multicast channel.

Unicast Adresses for sending to a single instance actor. in my Applikation Actors of any kind get their data from a unicast channel.

These files work for me ( i think ), but it may mutilate your hamster YMMV

You will need

  • TclUDP from Pat Thoyts, after some changes to udp from scotty/tnm i have not updated gin
  • util4gin.tcl
 #!/bin/tclsh
 # $Id: 13357,v 1.3 2006-01-12 07:01:23 jcw Exp $
 # file: util4gin.tcl
 # cprt: C2002 Uwe Klein Habertwedt
 #  lic:
 # what: simple async remote execution ala send to multiple clients
 # what: using udp multicast ( from Scotty/Tnm )
 # what: this has been tested on linux only
 # what: provide external functionality


 # either udp from tclUDP or from Tnm is used:
 # multicast functionality needs at least 3.0 ( IMHO )

 if {![ catch { package require udp } cerr ]} {
        puts stderr "tclUDP $cerr found"

 } elseif {![ catch { package require Tnm 3.0 } cerr ]} {
        puts stderr "Tnm $cerr found"
 } else {
        puts stderr "no udp found : cerr:\"$cerr\""
        puts [ package names ]
 }

 if {[ catch { namespace children Tnm } ]} {
        puts notthere
 } else {
        puts import
        catch { namespace import ::Tnm::* }
 }

 # lvarpop / lvarpush from Tclx is used:

 if {[ catch {package require Tclx } cerr ]} {
        proc lvarpop lvar {
                set ret [ uplevel lindex \$$lvar 0 ]
                uplevel set $lvar \[ lrange \$$lvar 1 end \]
                return $ret
        }
 }

 package provide util4gin  1.0
 return
 foreach cmd [ list udp lvarpop ] {
        if {[ info commands $cmd ] != $cmd } {
                puts stderr " \"$cmd\" not found"
                error "cmd $cmd not found"
        }
 }

 # OK:
  • gin.tcl
 #!/bin/tclsh
 # $Id: 13357,v 1.3 2006-01-12 07:01:23 jcw Exp $
 # simple async remote execution ala send to multiple clients
 # using udp multicast ( from tclUDP or Scotty/Tnm )
 # this has been tested on linux only

 # provide for udp and lvarpop:
 package require util4gin
 # source util4gin.tcl

 # find at end: package provide gin $version
 namespace eval gin {
        set rcsid { $Id: 13357,v 1.3 2006-01-12 07:01:23 jcw Exp $ }
        set version [ split [ lindex $rcsid 2 ] . ]
        variable ver [ join [ lrange  $version 0 1 ] . ]
        variable gin [ namespace current ]
        variable defaults { interface 0 port 0 listen info }
        variable config
        variable aliases
        variable aliaslist {    info 224.1.0.0:7770
                                data 224.1.0.0:7771
                                cmd  224.1.0.0:7772
                                log  224.1.0.0:7773
        }
        variable listen
        variable listenlist info
        variable send_hdl
        variable send_dest

        proc init {args} {
                variable defaults
                variable config
                variable listen
                variable listenlist
                variable aliases
                variable aliaslist
                variable send_hdl
                variable send_dest

                namespace export init listen unlisten \
                                 send trace  untrace \
                                 updt

                foreach {arg val} $defaults {
                        set config($arg) $val
                }
                foreach {arg val} $args {
                        set arg [ string trimleft $arg - ]
                        set config($arg) $val
                }
                foreach {al hp} $aliaslist {
                        alias $al $hp
                }
                foreach {hp} $listenlist {
                        listen $hp
                }
                set send_hdl [ udp_open $config(port) ]
                set send_dest {}
                fconfigure $send_hdl -buffering none -translation binary
                set shost $config(interface)
                set sport [ udp_conf $send_hdl -myport ]
                # set up proper if
                # puts "alias self $shost:$sport"
                alias self $shost:$sport
        }
        proc alias {args} {
                variable aliases
                foreach {al hp} $args {
                        set aliases(forw,$al)   $hp
                        set aliases(rev,$hp)    $al
                }
        }
        proc handle_dgram {host_port} {
                # puts stderr hit
                variable listen
                variable aliases

                set handle $listen($host_port)
                set msg  [ read $handle ]
                foreach {host port} [ udp_conf $handle -peer ] break

                if {[ catch { set ns $aliases(rev,${host}:$port)} ]} {
                        # puts aliasnotfound:$host:$port
                        set ns ${host}::$port
                }
                #D0#  puts stderr "gin::recv \"$ns\" $msg"
                if {$listen($host_port,enable)} {
                        if {[catch {namespace eval $ns $msg} cerr]} {
                                puts stderr $cerr
                        }
                }
        }
        proc send {args} {
                variable aliases
                variable send_hdl
                variable send_dest
                set dest [ lvarpop args ]
                catch { set dest $aliases(forw,$dest) }
                if { $dest != $send_dest } {
                        foreach {host port} [ split $dest : ] break
                        udp_conf $send_hdl $host $port
                        set send_dest $dest
                }
                puts -nonewline $send_hdl  $args
                #D0# puts stderr gin::send:[ list $args ]
        }
        proc listen {host_port args} {
                variable gin
                variable listen
                variable aliases
                if {[ array names listen $host_port ] == $host_port } {
                        return
                }
                set src $host_port
                catch { set src $aliases(forw,$src) }
                # puts listen:$host_port:$src
                foreach {host port} [ split $src : ] break
                set listen($host_port) [ udp_open -mcastadd $host $port ]
                set listen($host_port,enable) 1
                fconfigure $listen($host_port) \
                        -buffering none -translation binary
                # puts hp:$host:$port
                fileevent $listen($host_port) \
                        readable [ list ${gin}::handle_dgram $host_port ]
        }
        proc unlisten {host_port} {
                variable listen
                # puts unlisten:[ array names listen $host_port ]
                if {[ array names listen $host_port ] == {} } {
                        return
                }
                close $listen($host_port)
                unset listen($host_port)
        }
        proc mask {host_port} {
                if {[ catch { set ns $aliases(rev,$host_port)} ]} {
                        set ns $host_port
                }
                #D0# puts stderr gin::mask:$host_port:$ns
                namespace eval $ns {
                        proc set args {
                        }
                        proc array args {
                        }
                }

        }
        proc unmask {host_port} {
                if {[ catch { set ns $aliases(rev,$host_port)} ]} {
                        set ns $host_port
                }
                #D0# puts stderr gin::unmask:$host_port:$ns
                namespace eval $ns {
                        rename set {}
                        rename array {}
                }
        }
        proc tracevar {name svar dvar elems _var elem op} {
                if { $elem == {} } {
                        upvar $_var var
                        gin::send $name set $dvar $var
                } else {
                        upvar ${_var}($elem) var
                        gin::send $name set ${dvar}($elem) $var
                }
        }
        proc trace {name var args } {
                set svar [ lindex $var 0]
                set dvar [ lindex $var end]
                if {[ array exist $svar ]} {
                        if { $args == "" } {
                                set args *
                        }
                        ::trace variable $svar w [ list ::gin::tracevar $name $svar $dvar $args ]
                } else {
                        ::trace variable $svar w [ list ::gin::tracevar $name $svar $dvar ]
                }
        }
        proc untrace {name var} {
                set svar [ lindex $var 0]
                set dvar [ lindex $var end]
                ::trace vdelete $svar w [ list ::gin::tracevar $name $svar $dvar ]
        }
        proc updt {name arr {elems {}} } {
                set src [ lindex $arr 0]
                set dest [ lindex $arr end]
                if { $elems == {} } {
                        gin::send $name array set $dest [ \
                                array get $src \
                        ]
                } else {
                        gin::send $name array set $dest [ \
                                array get $src $elems \
                        ]
                }
        }
        puts stderr ver:$ver
        package provide gin $ver
 }

 #end
  • pgkIndex.tcl
 # Tcl package index file, version 1.1
 # This file is generated by the "pkg_mkIndex -lazy" command
 # and sourced either when an application starts up or
 # by a "package unknown" script.  It invokes the
 # "package ifneeded" command to set up package-related
 # information so that packages will be loaded automatically
 # in response to "package require" commands.  When this
 # script is sourced, the variable $dir must contain the
 # full path name of this file's directory.

 package ifneeded gin 1.1 [list source [file join $dir gin.tcl]]
 package ifneeded util4gin 1.0 [list source [file join $dir util4gin.tcl]]
  • using GIN
 package require gin
 gin::init
 if $src {
    set count 0
    while 1 {
       incr count
       gin::send info puts hallo nr$count
       after 1000
    }
 }
  • injecting scripts with bash
 echo "puts hallo from $USR" >/dev/udp/224.1.0.0/7770
  • sending and receiving from C
 notyet