modules/nns/server.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /*  ### ### ### ######### ######### #########*/
00003 /*  Name Service - Server (Singleton)*/
00004 
00005 /*  ### ### ### ######### ######### #########*/
00006 /*  Requirements*/
00007 
00008 package require Tcl 8.4
00009 package require comm             ; /*  Generic message transport*/
00010 package require interp           ; /*  Interpreter helpers.*/
00011 package require logger           ; /*  Tracing internal activity*/
00012 package require nameserv::common ; /*  Common/shared utilities*/
00013 
00014 namespace ::nameserv::server {}
00015 
00016 /*  ### ### ### ######### ######### #########*/
00017 /*  API: Start, Stop*/
00018 
00019 ret  ::nameserv::server::start () {
00020     variable comm
00021     variable port
00022     variable localonly
00023 
00024     log::debug "start"
00025     if {$comm ne ""} return
00026 
00027     log::debug "start /granted"
00028 
00029     set     interp [interp::createEmpty]
00030     foreach msg {
00031     Bind
00032     Release
00033     Search
00034     Search/Continuous/Start
00035     Search/Continuous/Stop
00036     ProtocolVersion
00037     ProtocolFeatures
00038     } {
00039     interp alias $interp $msg {} ::nameserv::server::$msg
00040     }
00041 
00042     set comm [comm::comm new ::nameserv::server::COMM \
00043           -interp $interp \
00044           -port   $port \
00045           -listen 1 \
00046           -local  $localonly]
00047 
00048     $comm hook lost ::nameserv::server::LOST
00049 
00050     log::debug "UP @$port local-only $localonly"
00051     return
00052 }
00053 
00054 ret  ::nameserv::server::stop () {
00055     variable comm
00056     variable names
00057     variable data
00058 
00059     log::debug "stop"
00060     if {$comm eq ""} return
00061 
00062     log::debug "stop /granted"
00063 
00064     # This kills all existing connection and destroys the configured
00065     # -interp as well.
00066 
00067     $comm destroy
00068     set comm ""
00069 
00070     array unset names *
00071     array unset data  *
00072 
00073     log::debug "DOWN"
00074     return
00075 }
00076 
00077 ret  ::nameserv::server::active? () {
00078     variable comm
00079     return [expr {$comm ne ""}]
00080 }
00081 
00082 /*  ### ### ### ######### ######### #########*/
00083 /*  INT: Protocol operations*/
00084 
00085 ret  ::nameserv::server::ProtocolVersion  () {return 1}
00086 ret  ::nameserv::server::ProtocolFeatures () {return {Core Search/Continuous}}
00087 
00088 ret  ::nameserv::server::Bind (type name , type cdata) {
00089     variable comm
00090     variable names
00091     variable data
00092 
00093     set id [$comm remoteid]
00094 
00095     log::debug "bind ([list $name -> $cdata]), for $id"
00096 
00097     if {[info exists data($name)]} {
00098     return -code error "Name \"$name\" is already bound"
00099     }
00100 
00101     lappend names($id)  $name
00102     set     data($name) $cdata
00103 
00104     Search/Continuous/NotifyAdd $name $cdata
00105     return
00106 }
00107 
00108 ret  ::nameserv::server::Release () {
00109     variable comm
00110     ReleaseId [$comm remoteid]
00111     return
00112 }
00113 
00114 ret  ::nameserv::server::Search (type pattern) {
00115     variable data
00116     return [array get data $pattern]
00117 }
00118 
00119 ret  ::nameserv::server::ReleaseId (type id) {
00120     variable names
00121     variable data
00122     variable searchi
00123 
00124     log::debug "release id $id"
00125 
00126     # Two steps. Release all searches the client may have open, then
00127     # all names it may have bound. That last step may trigger
00128     # notifications for searches by other clients. It must not trigger
00129     # searches from the client just going away, hence their release
00130     # first.
00131 
00132     foreach k [array names searchi [list $id *]] {
00133     Search/Release $k
00134     }
00135 
00136     if {[info exists names($id)]} {
00137     set gone {}
00138     foreach n $names($id) {
00139         lappend gone $n $data($n)
00140         catch {unset data($n)}
00141 
00142         log::debug "release name <$n>"
00143     }
00144     unset names($id)
00145 
00146     Search/Continuous/NotifyRelease $gone
00147     }
00148     return
00149 }
00150 
00151 /*  ### ### ### ######### ######### #########*/
00152 /*  Support for continuous and async searches*/
00153 
00154 ret  ::nameserv::server::Search/Continuous/Start (type tag , type pattern) {
00155     variable data
00156     variable searchi
00157     variable searchp
00158     variable comm
00159 
00160     set id [$comm remoteid]
00161 
00162     # Register the search, then generate the initial response.
00163     # Non-unique tags are silently discarded. Clients will wait
00164     # forever.
00165 
00166     set k [list $id $tag]
00167     if {[info exists searchi($k)]} return
00168 
00169     set searchi($k) $pattern
00170     lappend searchp($pattern) $k
00171 
00172     $comm send -async $id [list Search/Continuous/Change \
00173                    $tag add [array get data $pattern]]
00174     return
00175 }
00176 
00177 ret  ::nameserv::server::Search/Continuous/Stop (type tag) {
00178     Search/Release [list [$comm remoteid] $tag]
00179     return
00180 }
00181 
00182 ret  ::nameserv::server::Search/Release (type k) {
00183     variable searchi
00184     variable searchp
00185 
00186     # Remove search information from the data store
00187 
00188     if {![info exists searchi($k)]} return
00189 
00190     log::debug "release search <$k>"
00191 
00192     set pattern $searchi($k)
00193     unset searchi($k)
00194 
00195     set pos [lsearch -exact $searchp($pattern) $k]
00196     if {$pos < 0} return
00197     set new [lreplace $searchp($pattern) $pos $pos]
00198     if {[llength $new]} {
00199     # Shorten the callback list.
00200     set searchp($pattern) $new
00201     } else {
00202     # Nothing monitors that pattern anymore, remove it completely.
00203     unset searchp($pattern)
00204     }
00205     return
00206 }
00207 
00208 ret  ::nameserv::server::Search/Continuous/NotifyAdd (type name , type val) {
00209     variable searchp
00210 
00211     # Abort quickly if there are no searches waiting.
00212     if {![array size searchp]} return
00213 
00214     foreach p [array names searchp] {
00215     if {![string match $p $name]} continue
00216     Notify $p add [list $name $val]
00217     }
00218     return
00219 }
00220 
00221 ret  ::nameserv::server::Search/Continuous/NotifyRelease (type gone) {
00222     variable searchp
00223 
00224     # Abort quickly if there are no searches waiting.
00225     if {![array size searchp]} return
00226 
00227     array set m $gone
00228     foreach p [array names searchp] {
00229     set response [array get m $p]
00230     if {![llength $response]} continue
00231     Notify $p remove $response
00232     }
00233     return
00234 }
00235 
00236 ret  ::nameserv::server::Notify (type p , type type , type response) {
00237     variable searchp
00238     variable comm
00239 
00240     foreach item $searchp($p) {
00241     foreach {id tag} $item break
00242     $comm send -async $id \
00243         [list Search/Continuous/Change $tag $type $response]
00244     }
00245     return
00246 }
00247 
00248 /*  ### ### ### ######### ######### #########*/
00249 /*  Initialization - In-memory database*/
00250 
00251 namespace ::nameserv::server {
00252     /*  Database*/
00253     /*  search = list (id tag) : Searches are identified by client and a tag.*/
00254     /* */
00255     /*  array (id   -> list (name))      : Names under which a connection is known.*/
00256     /*  array (name -> data)             : Data associated with a name.*/
00257     /* */
00258     /*  array (pattern -> list (search)) : Per pattern the list of searches using it.*/
00259     /*  array (search -> pattern)        : Pattern per active search.*/
00260     /* */
00261     /*  searchp <~~> names*/
00262     /*  searchi <~~> data*/
00263 
00264     variable names   ; array  names =  {}
00265     variable data    ; array  data =   {}
00266     variable searchp ; array  searchp =  {}
00267     variable searchi ; array  searchi =  {}
00268 }
00269 
00270 /*  ### ### ### ######### ######### #########*/
00271 /*  INT: Connection management*/
00272 
00273 ret  ::nameserv::server::LOST (type args) {
00274     # Currently just to see when a client goes away.
00275 
00276     upvar 1 id id chan chan reason reason
00277     ReleaseId $id
00278     return
00279 }
00280 
00281 /*  ### ### ### ######### ######### #########*/
00282 /*  Initialization - System state*/
00283 
00284 namespace ::nameserv::server {
00285     /*  Object command of the communication channel of the server.*/
00286     /*  If present re-configuration is not possible.*/
00287 
00288     variable comm {}
00289 }
00290 
00291 /*  ### ### ### ######### ######### #########*/
00292 /*  API: Configuration management (host, port)*/
00293 
00294 ret  ::nameserv::server::cget (type option) {
00295     return [configure $option]
00296 }
00297 
00298 ret  ::nameserv::server::configure (type args) {
00299     variable localonly
00300     variable port
00301     variable comm
00302 
00303     if {![llength $args]} {
00304     return [list -localonly $localonly -port $port]
00305     }
00306     if {[llength $args] == 1} {
00307     # cget
00308     set opt [lindex $args 0]
00309     switch -exact -- $opt {
00310         -localonly { return $localonly }
00311         -port      { return $port }
00312         default {
00313         return -code error "bad option \"$opt\", expected -localonly, or -port"
00314         }
00315     }
00316     }
00317 
00318     # Note: Should -port be made configurable after communication has
00319     # started it might be necessary to provide code to re-initialize
00320     # the connections to all known clients using the new
00321     # configuration.
00322 
00323     while {[llength $args]} {
00324     set opt [lindex $args 0]
00325     switch -exact -- $opt {
00326         -localonly {
00327         if {[llength $args] % 2 == 1} {
00328             return -code error "value for \"$opt\" is missing"
00329         }
00330         # Todo: Check boolean 
00331         set new  [lindex $args 1]
00332         set args [lrange $args 2 end]
00333 
00334         if {$new == $localonly} continue
00335         set localonly $new
00336         if {$comm eq ""} continue
00337         $comm configure -local $localonly
00338         }
00339         -port {
00340         if {$comm ne ""} {
00341             return -code error "Unable to configure an active server"
00342         }
00343         if {[llength $args] % 2 == 1} {
00344             return -code error "value for \"$opt\" is missing"
00345         }
00346         # Todo: Check non-zero unsigned short integer
00347         set port [lindex $args 1]
00348         set args [lrange $args 2 end]
00349         }
00350         default {
00351         return -code error "bad option \"$opt\", expected -localonly, or -port"
00352         }
00353     }
00354     }
00355     return
00356 }
00357 
00358 /*  ### ### ### ######### ######### #########*/
00359 /*  Initialization - Tracing, Configuration*/
00360 
00361 logger::initNamespace ::nameserv::server
00362 namespace        ::nameserv::server {
00363     /*  Port the server will listen on, and boolean flag determining*/
00364     /*  acceptance of non-local connections.*/
00365 
00366     variable port      [nameserv::common::port]
00367     variable localonly 1
00368 }
00369 
00370 /*  ### ### ### ######### ######### #########*/
00371 /*  Ready*/
00372 
00373 package provide nameserv::server 0.3
00374 
00375 /** 
00376  * ### ### ### ######### ######### #########
00377 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1