source: tol/tolsh/tolsh.vfs/lib/tcllib1.10/nns/server.tcl

devel
Last change on this file was 8597867d, checked in by Jorge Pérez <josp.jorge@…>, 16 years ago

closes #976, se incluye tcllib1.10, base64 debe eliminarse del raiz de lib ya que esta incluido en tcllib

git-svn-id: ​https://www.tol-project.org/svn/tolp/trunk@2556 be4846ae-f051-483a-831f-b64e7d08527f

  • Property mode set to 100644
File size: 8.8 KB
Line 
1# -*- tcl -*-
2# ### ### ### ######### ######### #########
3## Name Service - Server (Singleton)
4
5# ### ### ### ######### ######### #########
6## Requirements
7
8package require Tcl 8.4
9package require comm             ; # Generic message transport
10package require interp           ; # Interpreter helpers.
11package require logger           ; # Tracing internal activity
12package require nameserv::common ; # Common/shared utilities
13
14namespace eval ::nameserv::server {}
15
16# ### ### ### ######### ######### #########
17## API: Start, Stop
18
19proc ::nameserv::server::start {} {
20    variable comm
21    variable port
22    variable localonly
23
24    log::debug "start"
25    if {$comm ne ""} return
26
27    log::debug "start /granted"
28
29    set     interp [interp::createEmpty]
30    foreach msg {
31        Bind
32        Release
33        Search
34        Search/Continuous/Start
35        Search/Continuous/Stop
36        ProtocolVersion
37        ProtocolFeatures
38    } {
39        interp alias $interp $msg {} ::nameserv::server::$msg
40    }
41
42    set comm [comm::comm new ::nameserv::server::COMM \
43                  -interp $interp \
44                  -port   $port \
45                  -listen 1 \
46                  -local  $localonly]
47
48    $comm hook lost ::nameserv::server::LOST
49
50    log::debug "UP @$port local-only $localonly"
51    return
52}
53
54proc ::nameserv::server::stop {} {
55    variable comm
56    variable names
57    variable data
58
59    log::debug "stop"
60    if {$comm eq ""} return
61
62    log::debug "stop /granted"
63
64    # This kills all existing connection and destroys the configured
65    # -interp as well.
66
67    $comm destroy
68    set comm ""
69
70    array unset names *
71    array unset data  *
72
73    log::debug "DOWN"
74    return
75}
76
77proc ::nameserv::server::active? {} {
78    variable comm
79    return [expr {$comm ne ""}]
80}
81
82# ### ### ### ######### ######### #########
83## INT: Protocol operations
84
85proc ::nameserv::server::ProtocolVersion  {} {return 1}
86proc ::nameserv::server::ProtocolFeatures {} {return {Core Search/Continuous}}
87
88proc ::nameserv::server::Bind {name cdata} {
89    variable comm
90    variable names
91    variable data
92
93    set id [$comm remoteid]
94
95    log::debug "bind ([list $name -> $cdata]), for $id"
96
97    if {[info exists data($name)]} {
98        return -code error "Name \"$name\" is already bound"
99    }
100
101    lappend names($id)  $name
102    set     data($name) $cdata
103
104    Search/Continuous/NotifyAdd $name $cdata
105    return
106}
107
108proc ::nameserv::server::Release {} {
109    variable comm
110    ReleaseId [$comm remoteid]
111    return
112}
113
114proc ::nameserv::server::Search {pattern} {
115    variable data
116    return [array get data $pattern]
117}
118
119proc ::nameserv::server::ReleaseId {id} {
120    variable names
121    variable data
122    variable searchi
123
124    log::debug "release id $id"
125
126    # Two steps. Release all searches the client may have open, then
127    # all names it may have bound. That last step may trigger
128    # notifications for searches by other clients. It must not trigger
129    # searches from the client just going away, hence their release
130    # first.
131
132    foreach k [array names searchi [list $id *]] {
133        Search/Release $k
134    }
135
136    if {[info exists names($id)]} {
137        set gone {}
138        foreach n $names($id) {
139            lappend gone $n $data($n)
140            catch {unset data($n)}
141
142            log::debug "release name <$n>"
143        }
144        unset names($id)
145
146        Search/Continuous/NotifyRelease $gone
147    }
148    return
149}
150
151# ### ### ### ######### ######### #########
152## Support for continuous and async searches
153
154proc ::nameserv::server::Search/Continuous/Start {tag pattern} {
155    variable data
156    variable searchi
157    variable searchp
158    variable comm
159
160    set id [$comm remoteid]
161
162    # Register the search, then generate the initial response.
163    # Non-unique tags are silently discarded. Clients will wait
164    # forever.
165
166    set k [list $id $tag]
167    if {[info exists searchi($k)]} return
168
169    set searchi($k) $pattern
170    lappend searchp($pattern) $k
171
172    $comm send -async $id [list Search/Continuous/Change \
173                               $tag add [array get data $pattern]]
174    return
175}
176
177proc ::nameserv::server::Search/Continuous/Stop {tag} {
178    Search/Release [list [$comm remoteid] $tag]
179    return
180}
181
182proc ::nameserv::server::Search/Release {k} {
183    variable searchi
184    variable searchp
185
186    # Remove search information from the data store
187
188    if {![info exists searchi($k)]} return
189
190    log::debug "release search <$k>"
191
192    set pattern $searchi($k)
193    unset searchi($k)
194
195    set pos [lsearch -exact $searchp($pattern) $k]
196    if {$pos < 0} return
197    set new [lreplace $searchp($pattern) $pos $pos]
198    if {[llength $new]} {
199        # Shorten the callback list.
200        set searchp($pattern) $new
201    } else {
202        # Nothing monitors that pattern anymore, remove it completely.
203        unset searchp($pattern)
204    }
205    return
206}
207
208proc ::nameserv::server::Search/Continuous/NotifyAdd {name val} {
209    variable searchp
210
211    # Abort quickly if there are no searches waiting.
212    if {![array size searchp]} return
213
214    foreach p [array names searchp] {
215        if {![string match $p $name]} continue
216        Notify $p add [list $name $val]
217    }
218    return
219}
220
221proc ::nameserv::server::Search/Continuous/NotifyRelease {gone} {
222    variable searchp
223
224    # Abort quickly if there are no searches waiting.
225    if {![array size searchp]} return
226
227    array set m $gone
228    foreach p [array names searchp] {
229        set response [array get m $p]
230        if {![llength $response]} continue
231        Notify $p remove $response
232    }
233    return
234}
235
236proc ::nameserv::server::Notify {p type response} {
237    variable searchp
238    variable comm
239
240    foreach item $searchp($p) {
241        foreach {id tag} $item break
242        $comm send -async $id \
243            [list Search/Continuous/Change $tag $type $response]
244    }
245    return
246}
247
248# ### ### ### ######### ######### #########
249## Initialization - In-memory database
250
251namespace eval ::nameserv::server {
252    # Database
253    # search = list (id tag) : Searches are identified by client and a tag.
254    #
255    # array (id   -> list (name))      : Names under which a connection is known.
256    # array (name -> data)             : Data associated with a name.
257    #
258    # array (pattern -> list (search)) : Per pattern the list of searches using it.
259    # array (search -> pattern)        : Pattern per active search.
260    #
261    # searchp <~~> names
262    # searchi <~~> data
263
264    variable names   ; array set names {}
265    variable data    ; array set data  {}
266    variable searchp ; array set searchp {}
267    variable searchi ; array set searchi {}
268}
269
270# ### ### ### ######### ######### #########
271## INT: Connection management
272
273proc ::nameserv::server::LOST {args} {
274    # Currently just to see when a client goes away.
275
276    upvar 1 id id chan chan reason reason
277    ReleaseId $id
278    return
279}
280
281# ### ### ### ######### ######### #########
282## Initialization - System state
283
284namespace eval ::nameserv::server {
285    # Object command of the communication channel of the server.
286    # If present re-configuration is not possible.
287
288    variable comm {}
289}
290
291# ### ### ### ######### ######### #########
292## API: Configuration management (host, port)
293
294proc ::nameserv::server::cget {option} {
295    return [configure $option]
296}
297
298proc ::nameserv::server::configure {args} {
299    variable localonly
300    variable port
301    variable comm
302
303    if {![llength $args]} {
304        return [list -localonly $localonly -port $port]
305    }
306    if {[llength $args] == 1} {
307        # cget
308        set opt [lindex $args 0]
309        switch -exact -- $opt {
310            -localonly { return $localonly }
311            -port      { return $port }
312            default {
313                return -code error "bad option \"$opt\", expected -localonly, or -port"
314            }
315        }
316    }
317
318    # Note: Should -port be made configurable after communication has
319    # started it might be necessary to provide code to re-initialize
320    # the connections to all known clients using the new
321    # configuration.
322
323    while {[llength $args]} {
324        set opt [lindex $args 0]
325        switch -exact -- $opt {
326            -localonly {
327                if {[llength $args] % 2 == 1} {
328                    return -code error "value for \"$opt\" is missing"
329                }
330                # Todo: Check boolean
331                set new  [lindex $args 1]
332                set args [lrange $args 2 end]
333
334                if {$new == $localonly} continue
335                set localonly $new
336                if {$comm eq ""} continue
337                $comm configure -local $localonly
338            }
339            -port {
340                if {$comm ne ""} {
341                    return -code error "Unable to configure an active server"
342                }
343                if {[llength $args] % 2 == 1} {
344                    return -code error "value for \"$opt\" is missing"
345                }
346                # Todo: Check non-zero unsigned short integer
347                set port [lindex $args 1]
348                set args [lrange $args 2 end]
349            }
350            default {
351                return -code error "bad option \"$opt\", expected -localonly, or -port"
352            }
353        }
354    }
355    return
356}
357
358# ### ### ### ######### ######### #########
359## Initialization - Tracing, Configuration
360
361logger::initNamespace ::nameserv::server
362namespace eval        ::nameserv::server {
363    # Port the server will listen on, and boolean flag determining
364    # acceptance of non-local connections.
365
366    variable port      [nameserv::common::port]
367    variable localonly 1
368}
369
370# ### ### ### ######### ######### #########
371## Ready
372
373package provide nameserv::server 0.3
374
375##
376# ### ### ### ######### ######### #########
Note: See TracBrowser for help on using the repository browser.