| 1 | # -*- tcl -*- |
|---|
| 2 | # ### ### ### ######### ######### ######### |
|---|
| 3 | ## Name Service - Client side connection monitor |
|---|
| 4 | |
|---|
| 5 | # ### ### ### ######### ######### ######### |
|---|
| 6 | ## Requirements |
|---|
| 7 | |
|---|
| 8 | package require nameserv ; # Name service client-side core |
|---|
| 9 | package require uevent ; # Watch for connection-loss |
|---|
| 10 | |
|---|
| 11 | namespace eval ::nameserv::auto {} |
|---|
| 12 | |
|---|
| 13 | # ### ### ### ######### ######### ######### |
|---|
| 14 | ## API: Write, Read, Search |
|---|
| 15 | |
|---|
| 16 | ## TODO - Keep after handle, ensure that only one poll is running. |
|---|
| 17 | ## Factor into smaller commands with descriptive names ... We have |
|---|
| 18 | ## several near-replicated pieces of code (error handling). |
|---|
| 19 | |
|---|
| 20 | proc ::nameserv::auto::bind {name data} { |
|---|
| 21 | # See nameserv::bind. Remembers the information, for re-binding |
|---|
| 22 | # when the connection was lost, and later restored. |
|---|
| 23 | |
|---|
| 24 | variable bindings |
|---|
| 25 | variable delay |
|---|
| 26 | |
|---|
| 27 | # Watch base client for loss of connection. |
|---|
| 28 | uevent::bind nameserv lost-connection ::nameserv::auto::Reconnect |
|---|
| 29 | |
|---|
| 30 | if {[catch { |
|---|
| 31 | nameserv::bind $name $data |
|---|
| 32 | } msg]} { |
|---|
| 33 | if {[string match *No name server*]} { |
|---|
| 34 | # No nameserver. Remember, and start reconnect polling. |
|---|
| 35 | set bindings($name) $data |
|---|
| 36 | after $delay ::nameserv::auto::Reconnect |
|---|
| 37 | return |
|---|
| 38 | } |
|---|
| 39 | # Name is bound already, lost immediately, generate |
|---|
| 40 | # standard event. |
|---|
| 41 | |
|---|
| 42 | uevent::generate nameserv lost-name [list name $name data $data] |
|---|
| 43 | return |
|---|
| 44 | } |
|---|
| 45 | |
|---|
| 46 | # Success. Remember for possible loss of connection. |
|---|
| 47 | set bindings($name) $data |
|---|
| 48 | return |
|---|
| 49 | } |
|---|
| 50 | |
|---|
| 51 | proc ::nameserv::auto::Reconnect {args} { |
|---|
| 52 | # args = <>|<tags event details> |
|---|
| 53 | # <tag,event> = <'nameserv','lost'> |
|---|
| 54 | # details = dict ('reason' -> string) |
|---|
| 55 | |
|---|
| 56 | if {![catch { |
|---|
| 57 | ::nameserv::server_features |
|---|
| 58 | }]} {Rebind ; return} |
|---|
| 59 | |
|---|
| 60 | variable delay |
|---|
| 61 | after $delay ::nameserv::auto::Reconnect |
|---|
| 62 | return |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | proc ::nameserv::auto::Rebind {} { |
|---|
| 66 | variable bindings |
|---|
| 67 | |
|---|
| 68 | foreach {name data} [array get bindings] { |
|---|
| 69 | if {[catch { |
|---|
| 70 | nameserv::bind $name $data |
|---|
| 71 | } msg]} { |
|---|
| 72 | # Lost server while rebinding names. Abort and wait for |
|---|
| 73 | # the reconnect to try again. |
|---|
| 74 | if {[string match *No name server*]} break |
|---|
| 75 | |
|---|
| 76 | # Other error => (name already bound) That means someone |
|---|
| 77 | # else took the name while we were not connected to the |
|---|
| 78 | # service. Best effort we can do: Deliver total loss of |
|---|
| 79 | # this binding to observers via event. |
|---|
| 80 | |
|---|
| 81 | uevent::generate nameserv lost-name [list name $name data $data] |
|---|
| 82 | } |
|---|
| 83 | } |
|---|
| 84 | return |
|---|
| 85 | } |
|---|
| 86 | |
|---|
| 87 | # ### ### ### ######### ######### ######### |
|---|
| 88 | ## Initialization - System state |
|---|
| 89 | |
|---|
| 90 | namespace eval ::nameserv::auto { |
|---|
| 91 | # In-memory database of bindings to restore after connection was |
|---|
| 92 | # lost and restored. |
|---|
| 93 | |
|---|
| 94 | variable bindings ; array set bindings {} |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | # ### ### ### ######### ######### ######### |
|---|
| 98 | ## API: Configuration management (host, port) |
|---|
| 99 | |
|---|
| 100 | proc ::nameserv::auto::cget {option} { |
|---|
| 101 | return [configure $option] |
|---|
| 102 | } |
|---|
| 103 | |
|---|
| 104 | proc ::nameserv::auto::configure {args} { |
|---|
| 105 | variable delay |
|---|
| 106 | |
|---|
| 107 | if {![llength $args]} { |
|---|
| 108 | return [list -delay $delay] |
|---|
| 109 | } |
|---|
| 110 | if {[llength $args] == 1} { |
|---|
| 111 | # cget |
|---|
| 112 | set opt [lindex $args 0] |
|---|
| 113 | switch -exact -- $opt { |
|---|
| 114 | -delay { return $delay } |
|---|
| 115 | default { |
|---|
| 116 | return -code error "bad option \"$opt\", expected -delay" |
|---|
| 117 | } |
|---|
| 118 | } |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | while {[llength $args]} { |
|---|
| 122 | set opt [lindex $args 0] |
|---|
| 123 | switch -exact -- $opt { |
|---|
| 124 | -delay { |
|---|
| 125 | if {[llength $args] % 2 == 1} { |
|---|
| 126 | return -code error "value for \"$opt\" is missing" |
|---|
| 127 | } |
|---|
| 128 | # TODO: check integer > 0 |
|---|
| 129 | set delay [lindex $args 1] |
|---|
| 130 | set args [lrange $args 2 end] |
|---|
| 131 | } |
|---|
| 132 | default { |
|---|
| 133 | return -code error "bad option \"$opt\", expected -delay" |
|---|
| 134 | } |
|---|
| 135 | } |
|---|
| 136 | } |
|---|
| 137 | return |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | # ### ### ### ######### ######### ######### |
|---|
| 141 | ## Initialization - Tracing, Configuration |
|---|
| 142 | |
|---|
| 143 | logger::initNamespace ::nameserv::auto |
|---|
| 144 | namespace eval ::nameserv::auto { |
|---|
| 145 | # Interval between reconnection attempts when connection was lost. |
|---|
| 146 | |
|---|
| 147 | variable delay 1000 ; # One second |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | # ### ### ### ######### ######### ######### |
|---|
| 151 | ## Ready |
|---|
| 152 | |
|---|
| 153 | package provide nameserv::auto 0.1 |
|---|
| 154 | |
|---|
| 155 | ## |
|---|
| 156 | # ### ### ### ######### ######### ######### |
|---|