source: tol/tolsh/tolsh.vfs/lib/tolcomm/tolserver.tcl @ 6d7779d

1941-docker-image-to-testdevel
Last change on this file since 6d7779d was 6d7779d, checked in by Jorge Pérez <josp.jorge@…>, 18 years ago

initial import

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

  • Property mode set to 100644
File size: 15.2 KB
Line 
1###############################################################################
2#
3#  tolserver.tcl -- communication layer of tolsh when acting as a server.
4#
5#  Originally taken from:
6#
7#    - Network server application template: http://wiki.tcl.tk/8301
8#    - fconfigure : http://wiki.tcl.tk/1469
9#
10###############################################################################
11
12package provide tolserver 1.0
13
14package require tlogger
15
16###############################################################################
17#
18#  tolserver - namespace to encapsulate the server state and behaviour
19#
20###############################################################################
21namespace eval ::tolserver {
22
23  variable data
24 
25  array set data {
26    socket       ""
27    queue,id     0
28    queue,tasks  ""
29    queue,cancel ""
30  }
31}
32
33###############################################################################
34#
35#  is_implemented -- check is a given proc is implemented in the
36#  server namespace.
37#
38#  Arguments:
39#
40#    command - a possible command name
41#
42#  Return:
43#
44#    a true value if command is implemented in for the server
45#    namespace a false value in other case.
46#
47###############################################################################
48proc ::tolserver::is_implemented { command } {
49  llength [info command [namespace current]::$command]
50}
51
52###############################################################################
53#
54#  get_client_id -- return the client host identification host:port
55#
56#  Arguments:
57#
58#    chan - the client socket channel for which host identification is requested
59#
60#  Return:
61#
62#    A string "host:port" identifying the client host for which 'chan'
63#    is connected.
64#
65###############################################################################
66proc ::tolserver::get_client_id { chan } {
67
68  upvar \#0 ::tolserver::${chan}_data channel_data
69 
70  return "$channel_data(host):$channel_data(port)"
71}
72
73###############################################################################
74#
75#  log -- write a log message into the server log file
76#
77#  This proc write a message into the server log channel which is
78#  handled by tlogger
79#
80#  Arguments:
81#
82#    msg - the message to be written
83#
84###############################################################################
85proc ::tolserver::log { level msg } {
86 
87  ::tlogger::log "tolserver" $level $msg
88}
89
90###############################################################################
91#
92#  register_client -- Configure a client channel
93#
94#    This proc is the server listener (see server socket creation),
95#    and is invoked whenever a client open a socket connection to this
96#    server.
97#
98#  Arguments:
99#
100#    chan - channel assigned to the client just conneted
101#    host - host address from where the channel was connected
102#    port - port from where the channel was connected in the host
103# 
104###############################################################################
105proc ::tolserver::register_client {chan host port} {
106
107  # We want to read whole lines
108  #
109  fconfigure $chan -blocking 0 -buffering line
110
111  log debug [fconfigure $chan]
112
113  # Invoke a proc to notify us that a client has registered.
114  #
115  on_client_connected $chan $host $port
116 
117  # Set up a fileevent to handle input
118  #
119  fileevent $chan readable \
120      [namespace code "read_from_client $chan"]
121}
122
123###############################################################################
124#
125#  read_from_client -- Read a complete data buffer from client
126#
127#  This proc is an event handler triggered whenever some data is
128#  available from the client channel.
129#
130#  Arguments:
131#
132#    chan - client channel
133#
134###############################################################################
135proc ::tolserver::read_from_client { chan } {
136
137  if {![eof $chan]} {
138    # Something could be read
139    #
140    if {[gets $chan buffer] == -1} {
141      # Only handle complete lines
142      #
143      return           
144    }
145  } else {
146    # Close up the channel and notify us that a client was disconnected.
147    #
148    on_eof_channel $chan
149    return
150  }
151
152  # process the buffer of data
153  #
154  on_client_request $chan $buffer
155}
156
157###############################################################################
158#
159#  on_client_request -- Process a buffer of data read from client.
160#
161#  Given a request 'buffer' just read from a client channel 'chan' the
162#  corresponding command is invoked. A request buffer is a word command
163#  followed by its arguments. The word commands accepted are:
164#
165#    - PING       : check if I'm alive
166#    - TOLEVAL    : evaluate a tol expression.
167#    - RECONN     : reconnection request
168#    - TCANCEL    : the client wants to cancel a task due to timeout
169#    - SHUTDOWN   : shutdown this process server.
170#
171#  Arguments --
172#
173###############################################################################
174proc ::tolserver::on_client_request {chan buffer} {
175
176  # Log this request
177  #
178  log "notice" "on_client_request ([get_client_id $chan]) : $buffer"
179  set idrq "NOID"
180  if {![regexp {([^\s]+)\s(\d+)\s([^\s]+)(.*)$} $buffer ==> cmd size idrq str]} {
181    set reason "invalid request format in '$buffer'"
182    log critical $reason
183    on_bad_request $chan $idrq $reason
184    return
185  }
186  set str [string trim $str]
187  set rsize [string length $str]
188  if {$rsize!=$size} {
189    set reason "invalid argument size '$str'. Should be $size and it was $rsize"
190    log critical $reason
191    on_bad_request $chan $idrq $reason
192    return
193  }
194  switch -- [string toupper $cmd] {
195    PING {
196      on_ping $chan $idrq
197    }
198    TOLEVAL {
199        on_toleval $chan $idrq $str
200    }
201    RECONN {
202        on_reconnect $chan $idrq $str
203    }
204    SHUTDOWN {
205      on_shutdown $chan
206    }
207    TCANCEL {
208      on_cancel $chan $str
209    }
210    default {
211      on_bad_request $chan $idrq "unknown request '$cmd' in '$buffer'"
212    }
213  }
214}
215
216proc ::tolserver::on_bad_request { chan idrq reason } {
217  log notice "bad request '$idrq' '$reason'"
218  send_to_client $chan "CRITICAL" $reason
219}
220
221###############################################################################
222#
223#  send_to_client -- send data to the client channel
224#
225#  Arguments:
226#
227#    chan - client socket channel who should be waiting for a server answer.
228#    TAG   - identification of the data
229#    data - data to be send to client.
230#
231###############################################################################
232proc ::tolserver::send_to_client { chan TAG {data ""} } {
233
234  log "debug" "enter : send_to_client $chan $TAG '$data'"
235
236  if {[catch {
237    puts $chan [string trimright "$TAG [string length $data] $data"]
238    flush $chan
239  } msg]} {
240    log emergency $msg
241  }
242
243  log "debug" "leave : send_to_client"
244}
245
246###############################################################################
247#
248#  on_ping -- check is the server is responding
249#
250#  Arguments:
251#
252#    chan - client socket channel who should be waiting for a server answer.
253#
254###############################################################################
255proc ::tolserver::on_ping { chan idrq } {
256
257  log "debug" "enter : on_ping $chan $idrq"
258
259  send_to_client $chan ACKPING $idrq
260
261  log "debug" "leave : on_ping $chan $idrq"
262}
263
264###############################################################################
265#
266#  on_toleval -- Invoked in response to a TOLEVAL request
267#
268#  This proc evaluate a tol expression requested from client connected
269#  at the oder end of channel $chan. The tol expression is contained
270#  in argument cmd. First the expression is inserted into a queue and
271#  a requestd ID is returned to the client. When idle the server will
272#  process the tol expressions waiting for being evaluated. After and
273#  expression evaluation is done a TFINISH is sent to the
274#  corresponding client.
275#
276#  Arguments:
277#
278#    chan -- client channel from where the RUN request was sent.
279#    idreq -- request ID, must be send back when evaluation is finished
280#    texpr  -- the tol expression to be executed.
281#
282###############################################################################
283proc ::tolserver::on_toleval { chan idrq cmd }  {
284
285  log "debug" "enter, on_toleval $chan $idrq $cmd"
286
287  log "notice" "TOLEVAL $cmd"
288  set idtask [insert_expr $chan $idrq $cmd]
289  send_to_client $chan "TSTART" "$idrq $idtask"
290  schedule_queue
291 
292  log "debug" "leave, on_toleval"
293}
294
295proc ::tolserver::on_reconnect { chan idrq idtask } {
296  # NO ESTA IMPLEMENTADO
297}
298
299###############################################################################
300#
301#  on_shutdown -- Handler for shutdown request
302#
303#  This proc perfom will shutdown this tol server. Before dying it
304#  will send an abort signal to all clients waiting for a request to
305#  finish.
306#
307#  Arguments:
308#
309#    chan - client socket channel from where the KILL request was sent.
310#    PID  - process ID to kill.
311#
312#  Return:
313#
314###############################################################################
315proc ::tolserver::on_shutdown { chan }  {
316
317  upvar \#0 ::tolserver::${chan}_data channel_data
318
319  log "debug" "enter, on_shutdown $chan"
320
321  log information "shutdown from $chan [get_client_id $chan]"
322  broadcast $chan "ACKSD"
323
324  after idle shutdown
325
326  log "debug" "leave, on_shutdown $chan"
327}
328
329###########################################################################
330#
331#
332###########################################################################
333proc ::tolserver::on_cancel { chan idrq } {
334
335  variable data
336
337  log notice "TCANCEL [get_client_id $chan] $idrq"
338
339  lappend data(queue,cancel) $idrq
340}
341
342###############################################################################
343#
344#  on_eof_channel -- close a client channel normally after an eof received.
345#
346#  This proc perform the following action:
347#
348#    - File event associated to the channel is cleared.
349#    - The client channel is closed.
350#    - The event is logged.
351#    - The channel specific data are cleared.
352#
353###############################################################################
354proc ::tolserver::on_eof_channel { chan } {
355  upvar \#0 ::tolserver::${chan}_data channel_data
356
357  log "debug" "enter, on_eof_channel $chan"
358  # Clear the file event.
359  #
360  fileevent $chan readable {}
361
362  # Close the client channel.
363  #
364  catch {close $chan}
365
366  # Log the situation.
367  #
368  log "notice" "$chan attached to [get_client_id $chan] has being closed"
369
370  # Clear the channel specific data.
371  #
372  array unset channel_data
373}
374
375###############################################################################
376#
377#  on_client_connected -- Invoked whenever the a client a connected.
378#
379#  Arguments:
380#
381#    chan - client channel just connected
382#    host - host address from where the channel was connected
383#    port - port from where the channel was connected in the host
384#
385###############################################################################
386proc ::tolserver::on_client_connected { chan host port } {
387  # Connection has been accepted. Log connection from client
388  #
389  log "debug" "enter, on_client_connected $chan $host $port"
390
391  log "notice" "Connection accepted from client ${host}:${port} through channel ${chan}"
392
393  # Create a variable for storing client channel specific data.
394  #
395  upvar \#0 ::tolserver::${chan}_data channel_data
396
397  set channel_data(host) $host
398  set channel_data(port) $port
399
400  log "debug" "leave, on_client_connected"
401}
402
403###############################################################################
404#
405#  open_socket -- Open the server socket
406#
407#  Arguments:
408#
409#    port - the port where this server will attend request from clients.
410#
411###############################################################################
412proc ::tolserver::open_socket { port } {
413  variable data
414
415  set data(socket) [socket -server [namespace code register_client] $port]
416}
417
418###############################################################################
419#
420#  close_socket -- Closes the server socket
421#
422###############################################################################
423proc ::tolserver::close_socket { } {
424
425  variable data
426
427  log "notice" "closing server socket ...."
428  catch {close $data(socket)}
429  set data(socket) ""
430}
431
432###############################################################################
433#
434#  start -- Start the server
435#
436#  Arguments:
437#
438#    port - the port where this server will attend request from clients.
439#
440###############################################################################
441proc ::tolserver::start { args } {
442  variable data
443
444  array set options {
445    -port 6669
446    -logfile ""
447  }
448  array set options $args
449
450  ::tlogger::init tolserver $options(-logfile)
451  ::tlogger::level tolserver "error"
452
453  # redirect the log
454  ::tol::console eval [string map "%P $options(-port)" {
455    Text {
456      Text srv_root = TolAppDataPath+"/server";
457      Real OSDirMake(srv_root);
458      Text PutDumpFile(srv_root+"/start.%P.log")
459    }
460  }]
461  if {[catch {open_socket $options(-port)} msgerr]} {
462    log emergency "::tolserver::start '$msgerr'"
463    exit
464  } else {
465    log "notice" "started"
466  }
467}
468
469###############################################################################
470#
471#  shutdown -- Perform shutdown task for the server.
472#
473###############################################################################
474proc ::tolserver::shutdown { } {
475  log "notice" "shutting down immediately"
476  close_socket
477  exit
478}
479
480###############################################################################
481#
482#  bgerror -- handle unxepected error detected in background
483#
484###############################################################################
485proc _bgerror {msg} {
486  # Handle unexpected errors here.
487  ::tolserver::log "emergency" "from _bgerror --> $msg"
488}
489
490proc ::tolserver::broadcast {chan what} {
491  set client [get_client_id $chan]
492  log "notice" "broadcasting $what requested from $client"
493  log "warn" "TO BE IMPLEMENTED"
494  # TO BE IMPLEMENTED
495}
496
497###########################################################################
498#
499#
500###########################################################################
501proc ::tolserver::insert_expr { chan idrq tol_expr } {
502  upvar \#0 ::tolserver::${chan}_data channel_data
503  variable data
504 
505  log "debug" "enter, insert_expr $chan $idrq $tol_expr"
506
507  incr data(queue,id)
508  set client [get_client_id $chan]
509  set idtask "${client}:$data(queue,id)"
510  lappend data(queue,tasks) [list $idtask $idrq $chan $tol_expr]
511
512  log "debug" "leave, insert_expr, $idtask"
513  set idtask
514}
515
516proc ::tolserver::process_queue { } {
517  variable data
518
519  log "debug" "enter, process_queue"
520
521  if {[llength $data(queue,tasks)]} {
522    set item [lindex $data(queue,tasks) 0]
523    set data(queue,tasks) [lrange data(queue,tasks) 1 end]
524    foreach {idtask idrq chan tol_expr} $item break
525    set idx [lsearch $data(queue,cancel) $idrq]
526    if {$idx != -1} {
527      log debug "canceling task $idtask"
528      # remove idrq from the cancel list
529      set data(queue,cancel) [lreplace $data(queue,cancel) $idx $idx]
530    } else {
531      log debug "processing task $idtask"
532      ::tol::console eval $tol_expr
533      # inform client this request has being finished
534      after idle [namespace code "send_to_client $chan TFINISH $idtask"]
535    }
536    schedule_queue
537  }
538
539  log "debug" "leave, process_queue"
540}
541
542###########################################################################
543#
544#
545#
546###########################################################################
547proc ::tolserver::schedule_queue { } {
548
549  variable data
550
551  if {[llength $data(queue,tasks)]} {
552    after idle [namespace code process_queue]
553  }
554}
Note: See TracBrowser for help on using the repository browser.