nns.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 package require snit ;
00014 package require uevent ;
00015
00016 namespace ::nameserv {}
00017
00018
00019
00020
00021 ret ::nameserv::bind (type name , type data) {
00022 # Registers this application at the configured name service under
00023 # the specified name, and provides a value.
00024 #
00025 # Note: The application is allowed register multiple names.
00026 #
00027 # Note: A registered name is automatically removed by the server
00028 # when the connection to it collapses.
00029
00030 DO Bind $name $data
00031 return
00032 }
00033
00034 ret ::nameserv::release () {
00035 # Releases all names the application is registered under
00036 # at the configured name service.
00037
00038 DO Release
00039 return
00040 }
00041
00042 ret ::nameserv::search (type args) {
00043 # Searches the configured name service for applications whose name
00044 # matches the given pattern. Returns a dictionary mapping from the
00045 # names to the data they provided at 'bind' time.
00046
00047 # In continuous and async modes it returns an object whose
00048 # contents reflect the current set of matching entries.
00049
00050 switch -exact [llength $args] {
00051 0 {
00052 set continuous 0
00053 set pattern *
00054 }
00055 1 {
00056 set opt [lindex $args 0]
00057 if {$opt eq "-continuous"} {
00058 set oneshot 0
00059 set continuous 1
00060 set pattern *
00061 } elseif {$opt eq "-async"} {
00062 set oneshot 1
00063 set continuous 1
00064 set pattern *
00065 } else {
00066 set continuous 0
00067 set pattern $opt
00068 }
00069 }
00070 2 {
00071 set opt [lindex $args 0]
00072 if {$opt eq "-continuous"} {
00073 set oneshot 0
00074 set continuous 1
00075 set pattern [lindex $args 1]
00076 } elseif {$opt eq "-async"} {
00077 set oneshot 1
00078 set continuous 1
00079 set pattern [lindex $args 1]
00080 } else {
00081 return -code error "wrong\#args: Expected ?-continuous|-async? ?pattern?"
00082 }
00083 }
00084 default {
00085 return -code error "wrong\#args: Expected ?-continuous|-async? ?pattern?"
00086 }
00087 }
00088
00089 if {$continuous} {
00090 variable search
00091 # This client uses the receiver object as tag for the search
00092 # in the service. This is easily unique, and makes dispatch of
00093 # incoming results later easy too.
00094
00095 set receiver [receiver %AUTO% $oneshot]
00096 ASYNC Search/Continuous/Start $receiver $pattern
00097
00098 set search($receiver) .
00099 return $receiver
00100 } else {
00101 return [DO Search $pattern]
00102 }
00103 }
00104
00105 ret ::nameserv::protocol () {
00106 return 1
00107 }
00108
00109 ret ::nameserv::server_protocol () {
00110 return [DO ProtocolVersion]
00111 }
00112
00113 ret ::nameserv::server_features () {
00114 return [DO ProtocolFeatures]
00115 }
00116
00117
00118
00119
00120 ret ::nameserv::DO (type args) {
00121 variable sid
00122 log::debug [linsert $args end @ $sid]
00123
00124 if {[catch {
00125 [SERV] send $sid $args
00126 #eval [linsert $args 0 [SERV] send $sid] ;# $args
00127 } msg]} {
00128 if {[string match "*refused*" $msg]} {
00129 return -code error "No name server present @ $sid"
00130 } else {
00131 return -code error $msg
00132 }
00133 }
00134 # Result of the call
00135 return $msg
00136 }
00137
00138 ret ::nameserv::ASYNC (type args) {
00139 variable sid
00140 log::debug [linsert $args end @ $sid]
00141
00142 if {[catch {
00143 [SERV] send -async $sid $args
00144 #eval [linsert $args 0 [SERV] send $sid] ;# $args
00145 } msg]} {
00146 if {[string match "*refused*" $msg]} {
00147 return -code error "No name server present @ $sid"
00148 } else {
00149 return -code error $msg
00150 }
00151 }
00152 # No result to return
00153 return
00154 }
00155
00156 ret ::nameserv::SERV () {
00157 variable comm
00158 variable sid
00159 variable host
00160 variable port
00161 if {$comm ne ""} {return $comm}
00162
00163 # NOTE
00164 # -local 1 means that clients can only talk to a local
00165 # name service. Might make sense to auto-force
00166 # -local 0 for host ne "localhost".
00167
00168 set interp [interp::createEmpty]
00169 foreach msg {
00170 Search/Continuous/Change
00171 } {
00172 interp alias $interp $msg {} ::nameserv::$msg
00173 }
00174
00175 set sid [list $port $host]
00176 set comm [comm::comm new ::nameserv::CSERV \
00177 -interp $interp \
00178 -local 1 \
00179 -listen 1]
00180
00181 $comm hook lost ::nameserv::LOST
00182
00183 log::debug [list SERV @ $sid : $comm]
00184 return $comm
00185 }
00186
00187 ret ::nameserv::LOST (type args) {
00188 upvar 1 id id chan chan reason reason
00189 variable comm
00190 variable sid
00191 variable search
00192
00193 log::debug [list LOST @ $sid - $reason]
00194
00195 $comm destroy
00196
00197 set comm {}
00198 set sid {}
00199
00200 # Notify async/cont search of the loss.
00201 foreach r [array names search] {
00202 $r DATA stop
00203 unset search($r)
00204 }
00205
00206 uevent::generate nameserv lost-connection [list reason $reason]
00207 return
00208 }
00209
00210
00211
00212
00213 namespace ::nameserv {
00214
00215
00216
00217
00218 variable comm {}
00219 variable sid {}
00220
00221
00222
00223 variable search ; array search = {}
00224 }
00225
00226
00227
00228
00229 ret ::nameserv::cget (type option) {
00230 return [configure $option]
00231 }
00232
00233 ret ::nameserv::configure (type args) {
00234 variable host
00235 variable port
00236
00237 if {![llength $args]} {
00238 return [list -host $host -port $port]
00239 }
00240 if {[llength $args] == 1} {
00241 # cget
00242 set opt [lindex $args 0]
00243 switch -exact -- $opt {
00244 -host { return $host }
00245 -port { return $port }
00246 default {
00247 return -code error "bad option \"$opt\", expected -host, or -port"
00248 }
00249 }
00250 }
00251
00252 if {$comm ne ""} {
00253 return -code error "Unable to configure an active connection"
00254 }
00255
00256 # Note: Should -port/-host be made configurable after
00257 # communication has started it will be necessary to provide code
00258 # which retracts everything from the old server and re-initializes
00259 # the new one.
00260
00261 while {[llength $args]} {
00262 set opt [lindex $args 0]
00263 switch -exact -- $opt {
00264 -host {
00265 if {[llength $args] % 2 == 1} {
00266 return -code error "value for \"$opt\" is missing"
00267 }
00268 set host [lindex $args 1]
00269 set args [lrange $args 2 end]
00270 }
00271 -port {
00272 if {[llength $args] % 2 == 1} {
00273 return -code error "value for \"$opt\" is missing"
00274 }
00275 set port [lindex $args 1]
00276 # Todo: Check non-zero unsigned short integer
00277 set args [lrange $args 2 end]
00278 }
00279 default {
00280 return -code error "bad option \"$opt\", expected -host, or -port"
00281 }
00282 }
00283 }
00284 return
00285 }
00286
00287
00288
00289
00290 ret ::nameserv::Search/Continuous/Change (type tag , type type , type response) {
00291
00292 # Ignore messages for searches which were canceled already.
00293 #
00294 # Due to the async nature of the messages for cont/async search
00295 # the client may have canceled the receiver object already, sent
00296 # the stop message already, but still has to process search
00297 # results which were already in flight. We ignore them.
00298
00299 if {![llength [info commands $tag]]} return
00300
00301 # This client uses the receiver object as tag, dispatch the
00302 # received notification to it.
00303
00304 $tag DATA $type $response
00305 return
00306 }
00307
00308 snit::type ::nameserv::receiver {
00309
00310 option -command -default {}
00311
00312 constructor {{once 0}} {
00313 singleshot = $once
00314 return
00315 }
00316
00317 destructor {
00318 if {$singleshot} return
00319 ASYNC Search/Continuous/Stop $self
00320 Callback stop {}
00321 return
00322 }
00323
00324 ret get (type k) {
00325 if {![info exists current($k)]} {return -code error "Unknown key \"$k\""}
00326 return $current($k)
00327 }
00328
00329 ret names () {
00330 return [array names current]
00331 }
00332
00333 ret size () {
00334 return [array size current]
00335 }
00336
00337 ret getall (optional pattern =*) {
00338 return [array get current $pattern]
00339 }
00340
00341 ret filled () {
00342 return $filled
00343 }
00344
00345 ret {DATA stop} () {
00346 if {$filled && $singleshot} return
00347 set singleshot 1 ; # Prevent 'stop' again during destruction.
00348 Callback stop {}
00349 return
00350 }
00351
00352 ret {DATA add} (type response) {
00353 set filled 1
00354 if {$singleshot} {
00355 ASYNC Search/Continuous/Stop $self
00356 }
00357 array set current $response
00358 Callback add $response
00359 if {$singleshot} {
00360 Callback stop {}
00361 }
00362 return
00363 }
00364
00365 ret {DATA remove} (type response) {
00366 set filled 1
00367 foreach {k v} $response {
00368 unset -nocomplain current($k)
00369 }
00370 Callback remove $response
00371 return
00372 }
00373
00374 ret Callback (type type , type response) {
00375 upvar 1 options options
00376 if {$options(-command) eq ""} return
00377 # Defer execution to event loop
00378 after 0 [linsert $options(-command) end $type $response]
00379 return
00380 }
00381
00382 variable singleshot 0
00383 variable current -array {}
00384 variable filled 0
00385 }
00386
00387
00388
00389
00390 logger::initNamespace ::nameserv
00391 namespace ::nameserv {
00392
00393
00394 variable host localhost
00395 variable port [nameserv::common::port]
00396 }
00397
00398
00399
00400
00401 package provide nameserv 0.3
00402
00403
00404
00405