source: tol/tolsh/tolsh.vfs/lib/tolcomm/tolserver.tcl

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

refs #1028, tequila server is started on the first available port starting from port number 20458.

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

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