modules/nns/server.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008 package require Tcl 8.4
00009 package require comm ;
00010 package require interp ;
00011 package require logger ;
00012 package require nameserv::common ;
00013
00014 namespace ::nameserv::server {}
00015
00016
00017
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
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
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
00250
00251 namespace ::nameserv::server {
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
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
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
00283
00284 namespace ::nameserv::server {
00285
00286
00287
00288 variable comm {}
00289 }
00290
00291
00292
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
00360
00361 logger::initNamespace ::nameserv::server
00362 namespace ::nameserv::server {
00363
00364
00365
00366 variable port [nameserv::common::port]
00367 variable localonly 1
00368 }
00369
00370
00371
00372
00373 package provide nameserv::server 0.3
00374
00375
00376
00377