Index: tolbaseBLT/lib/rmtps_client/pkgIndex.tcl
===================================================================
--- tolbaseBLT/lib/rmtps_client/pkgIndex.tcl	(revision 56ade3b7ecd6d1e9d7583c3b0b8f82416fdbe7e2)
+++ tolbaseBLT/lib/rmtps_client/pkgIndex.tcl	(revision 56ade3b7ecd6d1e9d7583c3b0b8f82416fdbe7e2)
@@ -0,0 +1,11 @@
+# Tcl package index file, version 1.1
+# This file is generated by the "pkg_mkIndex" command
+# and sourced either when an application starts up or
+# by a "package unknown" script.  It invokes the
+# "package ifneeded" command to set up package-related
+# information so that packages will be loaded automatically
+# in response to "package require" commands.  When this
+# script is sourced, the variable $dir must contain the
+# full path name of this file's directory.
+
+package ifneeded rmtps_client 1.0 [list source [file join $dir rmtps_client.tcl]]
Index: tolbaseBLT/lib/rmtps_client/rmtps_client.tcl
===================================================================
--- tolbaseBLT/lib/rmtps_client/rmtps_client.tcl	(revision 56ade3b7ecd6d1e9d7583c3b0b8f82416fdbe7e2)
+++ tolbaseBLT/lib/rmtps_client/rmtps_client.tcl	(revision 56ade3b7ecd6d1e9d7583c3b0b8f82416fdbe7e2)
@@ -0,0 +1,447 @@
+###############################################################################
+#
+#   rmtps_client.tcl -- Remote process control client side.
+#
+#   Implement the client communication routines with RmtPS_Server via
+#   sockets.
+#
+###############################################################################
+
+package provide rmtps_client 1.0
+
+package require base64
+
+if { [ catch { package require tlogger } ] } {
+  namespace eval tlogger {
+    variable _level "error"
+
+    proc init { svc {logfile ""} } {
+    }
+    
+    proc level { svc l } {
+      variable _level $l
+    }
+
+    proc log { svc l msg } {
+      variable _level 
+
+      if { $l eq $_level } {
+        puts $msg
+      }
+    }
+  }
+}
+
+###############################################################################
+#
+#   rmtps_client - encapsulate the client code and variable in a
+#   namespace
+#
+###############################################################################
+namespace eval ::rmtps_client {
+  # timeout to be used when waiting response from the server
+  variable timeout_id ""
+
+  # number of seconds to wait before canceling a server's request
+  # request, by default it is set to 3 seconds. If the maximun time is
+  # reached without answer from the server, the server channel is
+  # closed and the request results in an error with a timeout reason.
+  variable server_timeout 10
+
+  # variable to store the result read from the server
+  variable server_result
+
+  # reason of the last error
+  variable error_reason ""
+
+  variable asking_error 0
+
+  # Directory where I'm located. Will be used later to evaluate the
+  # client part written in TOL
+  #
+  variable mydir [file normalize [file dir [info script]]]
+
+  
+}
+
+proc ::rmtps_client::init { {logfile ""} } {
+  ::tlogger::init  rmtps_client $logfile
+  ::tlogger::level rmtps_client "error"
+}
+
+proc ::rmtps_client::log { level msg } {
+  
+  ::tlogger::log rmtps_client $level $msg
+}
+
+###############################################################################
+#
+#   get_error_reason --
+#
+#
+###############################################################################
+proc ::rmtps_client::get_error_reason { } {
+
+  variable error_reason
+  
+  set error_reason
+}
+
+###############################################################################
+#
+#   set_server_timeout -- change the current server's request timeout
+#
+#   Arguments --
+#
+#     timeout - maximun number of seconds to wait a request result from
+#     the server.
+#
+#   Return --
+#
+#     the previous timeout value
+#
+###############################################################################
+proc ::rmtps_client::set_server_timeout { timeout } {
+  variable server_timeout
+
+  old_timeout $server_timeout
+  set server_timeout $timeout
+  set old_timeout
+}
+
+###############################################################################
+#
+#   wait_result -- event to read result from server channel
+# 
+#     This procedure is invoked whenever an input is available on the
+#     server channel. Every routine must use this event handler in
+#     order to
+#
+#   Arguments --
+#
+#     chan - channel from which we are waiting a line response
+#
+#   Return --
+#
+###############################################################################
+proc ::rmtps_client::wait_result { chan } {
+  variable timeout_id
+  variable server_result
+  variable server_timeout 
+  variable error_reason
+
+  log "debug" "wait_result $chan"
+
+  if {![eof $chan]} {
+    if {[gets $chan data] == -1} {
+      log "debug" "wait_result : incomplete data"
+      return;			# only handle complete lines
+    }
+  } else {
+    # Close the channel.
+    #
+    after cancel $timeout_id
+    fileevent $chan readable {}
+    set error_reason "end of channel from server"
+    log "warn" "wait_result : end of channel from server"
+    # Cancel vwait
+    #
+    set server_result ""
+    return
+  }
+  after cancel $timeout_id
+  fileevent $chan readable {}
+  set error_reason ""
+  # Return the data via the vwaited variable
+  #
+  log "debug" "wait_result read : $data"
+  set server_result $data
+}
+
+###############################################################################
+#
+#   handle_timeout --
+#
+#
+###############################################################################
+proc ::rmtps_client::handle_timeout { chan } {
+  variable server_result
+  variable error_reason
+
+  log "debug" "handle_timeout $chan" 
+
+  # Set result from server to empty to return from vwait
+  #
+  set server_result ""
+  
+  # Set error reason to timeout
+  #
+  set error_reason "server timeout"
+}
+
+proc ask_server_channel { chan request } {
+
+  usado sobre todo para pedir la razon del fallo
+
+  el timeout quizas deba cerrar el channel, esa peticion se cancela
+
+  file channels puedo usarlo para verificar si un socket existe.
+}
+
+###############################################################################
+#
+#   ask_server --
+#
+#
+###############################################################################
+proc ::rmtps_client::ask_server { host port request } {
+
+  variable server_result
+  variable server_timeout
+  variable timeout_id
+  variable error_reason
+  variable asking_error
+
+  log "debug" "enter ask_server $host $port $request"
+
+  # open channel to server
+  #
+  set chan [socket $host $port]
+
+  # We want whole lines
+  #
+  fconfigure $chan -blocking 0 -buffering line
+
+  # Send the request to the server
+  #
+  puts $chan $request
+
+  # Remember that server is line oriented
+  #
+  flush $chan
+
+  # Set up a fileevent to handle input from server
+  # 
+  fileevent $chan readable [namespace code "wait_result $chan"]
+
+  # Set up timeout handler
+  #
+  set timeout_id [after [expr {1000*$server_timeout}] \
+                      [namespace code "handle_timeout $chan"]]
+  
+  # Wait for result or timeout
+  #
+  vwait ::rmtps_client::server_result
+  
+  catch {close $chan}
+
+  log "debug" "enter ask_server $host $port $server_result"
+  
+  set server_result
+}
+
+###############################################################################
+#
+#   server_ping -- Check if a rmtps_server is listening on a remote
+#   host
+#
+#   Arguments --
+#
+#     host - computer where RmtPS_Server is running
+#     port - port where the server socket should be listening
+#
+#   Return --
+#
+#     1 if rmtps_server is running and listening
+#     0 if rmtps_server does not answer or give back the wrong answer
+#
+###############################################################################
+proc ::rmtps_client::server_ping { host port } {
+
+  log "debug" "enter server_ping $host $port"
+
+  # send request to server
+  #
+  set ans [ask_server $host $port "PING"]
+  set result [expr {$ans eq "RMTPS_SERVER WORKING"}]
+
+  log "debug" "leave server_ping $host $port"
+
+  set result
+}
+
+###############################################################################
+#
+#   ps_is_active -- Check if a process is active on a remote host
+#
+#   Arguments --
+#
+#     host - computer where RmtPS_Server is running
+#     port - port where the server socket is listening
+#     PID  - process identifier to check for activity
+#
+#   Return --
+#
+#     1 if process given by PID is active
+#     0 if process given by PID is not active
+#    -1 there was an error during the operation, don't know if its alive
+#
+###############################################################################
+proc ::rmtps_client::ps_is_active { host port PID } {
+
+  log "debug" "enter ps_is_active $host $port $PID"
+
+  # send request to server
+  #
+  set result [ask_server $host $port "ALIVE $PID"]
+
+  log "debug" "from ask_server = '$result'"
+
+  # Answer from server must be an integer value
+  #
+  if { $result eq "" || ![string is integer $result] ||
+      ![string is boolean $result] } {
+    set result -1
+  } else {
+    # Make sure 0 or 1 is returned
+    #
+    set result [expr {!!$result}]
+  }
+
+  log "debug" "leave ps_is_active $host $port $PID : $result"
+
+  set result
+}
+
+###############################################################################
+#
+#  ps_kill -- terminate a given process on a remote host
+#
+#   Arguments --
+#
+#     host - computer where RmtPS_Server is running
+#     port - port where the server socket is listening
+#     PID  - process identifier to terminate
+#
+#   Return --
+#
+#     1 if process given by PID could be terminated
+#     0 if process given by PID could not be terminated
+#
+#    -1 if there was an error during the kill, in any case don't know
+#       is the process was killed
+#
+###############################################################################
+proc ::rmtps_client::ps_kill { host port PID } {
+
+  log "debug" "enter ps_kill $host $port $PID"
+
+  # send request to server
+  #
+  set result [ask_server $host $port "KILL $PID"]
+  # Answer from server must be an integer or boolean value
+  #
+  if {![string is integer $result]} {
+    set result -1
+  } else {
+    # Make sure 0, 1 or -1 is returned
+    #
+    set result [expr {$result==-1?-1:$result!=0}]
+  }
+
+  log "debug" "leave ps_kill $host $port $PID : $result"
+
+  set result
+}
+
+###############################################################################
+#
+#  ps_run -- execute a program on a remote host
+#
+#   Arguments --
+#
+#     host     - computer (remote host) where RmtPS_Server is running
+#     port     - port where the server socket is listening
+#     cmdline  - command line (program + arguments) to run on a remote host
+#
+#   Return --
+#
+#     PID of the process if cmdline could be executed
+#     0 if the command line could not be executed
+#
+###############################################################################
+proc ::rmtps_client::ps_run { host port cmdline } {
+
+  log "debug" "enter ps_run $host $port $cmdline"
+
+  # send request to server
+  #
+  set result [ask_server $host $port "RUN $cmdline"]
+  # Make sure that 0 or PID>0 is returned
+  #
+  if {![string is integer $result] || $result<0} {
+    set result 0
+  }
+
+  log "debug" "leave ps_run $host $port $cmdline : $result"
+
+  set result
+}
+
+
+###############################################################################
+#
+#  ps_runtol -- execute a TOL process on a remote host
+#
+#   Arguments --
+#
+#     host        - computer (remote host) where RmtPS_Server is running
+#     port        - port where the server socket is listening
+#     cmdln_args  - TOL arguments
+#
+#   Return --
+#
+#     PID of the process if cmdline could be executed
+#     0 if the command line could not be executed
+#
+###############################################################################
+proc ::rmtps_client::ps_runtol { host port cmdln_args } {
+
+  log "debug" "enter ps_runtol $host $port $cmdln_args"
+
+  # send request to server
+  #
+  set cmdln64 [ base64::encode -wrapchar "" $cmdln_args ]
+  #set cmdln64 $cmdln_args
+  set result [ask_server $host $port "RUNTOL $cmdln64"]
+  # Make sure that 0 or PID>0 is returned
+  #
+  if {![string is integer $result] || $result<0} {
+    set result 0
+  }
+
+  log "debug" "leave ps_runtol $host $port $cmdln_args : $result"
+
+  set result
+}
+
+###############################################################################
+#
+#  define_TOLAPI --
+#
+#  This proc will evaluate in the protected stack the TOL code
+#  corresponding to the client part of rmtps.
+#
+###############################################################################
+proc ::rmtps_client::define_TOLAPI { } {
+  variable mydir
+
+  # check before if rmtps_client.tol has being loaded
+  #
+  if {[catch {::tol::info variable Code RemoteProcessInitTcl}]} {
+    set fd [open [file join $mydir "rmtps_client.tol"]]
+    set tol_code [read $fd]
+    close $fd
+    tol::console eval -hidden $tol_code
+  }
+}
Index: tolbaseBLT/lib/rmtps_client/rmtps_client.tol
===================================================================
--- tolbaseBLT/lib/rmtps_client/rmtps_client.tol	(revision 56ade3b7ecd6d1e9d7583c3b0b8f82416fdbe7e2)
+++ tolbaseBLT/lib/rmtps_client/rmtps_client.tol	(revision 56ade3b7ecd6d1e9d7583c3b0b8f82416fdbe7e2)
@@ -0,0 +1,260 @@
+/* -*- mode: c++ -*- */
+/**
+ * rmtps_client.tol --
+ * 
+ */
+
+/**
+ *  RemotePing -- Verifica si un servidor RMTPS esta escuchando
+ *
+ *  Argumentos:
+ *
+ *    Host: host que aloja al servidor RMTPS
+ *    
+ *    Port: puerto donde el servidor RMPTS esta escuchando
+ *
+ *  Resultado:
+ *  
+ */
+Real RemotePing(Text Host, Real Port)
+{
+
+  Set result = Tcl_Eval("::rmtps_client::server_ping "+Host+" "+
+                        FormatReal(Port, "%.0lf"));
+  Real If(result["status"], Eval(result["result"]),{
+      WriteLn("RemotePing: "+result["result"],"E");
+      Real 0
+    })
+};
+PutDescription("Verifica si un servidor RMTPS_SERVER esta ejecutandose en un "
+               "ordenador remoto y esta escuchando peticiones."
+               "Retorna 1 si el servidor esta ejecutandose o 0 en caso "
+               "contrario",
+               RemotePing);
+
+/**
+ *  RemoteExec -- Ejecuta un comando en remoto
+ *
+ *  Argumentos:
+ *
+ *    Host: host que aloja al servidor RMTPS
+ *    
+ *    Port: puerto donde el servidor RMPTS esta escuchando
+ *
+ *    CmdLine:
+ *
+ *  Resultado:
+ *  
+ */
+Real RemoteExec(Text Host, Real Port, Text CmdLine)
+{
+
+  Set result = Tcl_Eval("::rmtps_client::ps_run "+Host+" "+
+                        FormatReal(Port, "%.0lf")+
+                        " {"+CmdLine+"}");
+  Real If(result["status"], Eval(result["result"]),{
+      WriteLn("RemoteExec: "+result["result"],"E");
+      Real 0
+    })
+};
+PutDescription("Ejecuta en un ordenador remoto un comando dado en CmdLine. "
+               "CmdLine tambien contiene los argumentos separados por "
+               "espacios. "
+               "Retorna el PID del proceso ejecutado o 0 si algun error "
+               "ocurrio durante el intento de ejecucion del comando. "
+               "Necesita que un servidor RmtPS_Server este escuchando en el "
+               "puerto Port del ordenador Host",
+               RemoteExec);
+
+/**
+ *  RemoteTOL -- Invoca un proceso TOL remoto
+ *
+ *  Argumentos:
+ *
+ *    Host: host que aloja al servidor RMTPS
+ *    
+ *    Port: puerto donde el servidor RMPTS esta escuchando
+ *
+ *    Args:
+ *
+ *  Resultado:
+ *
+ *    PID del proceso TOL lanzado o 0 si algun error ha ocurrido
+ *  
+ */
+Real RemoteTOL(Text Host, Real Port, Set Args )
+{
+
+  Text cmd = "::rmtps_client::ps_runtol";
+  Text port = FormatReal(Port, "%.0lf");
+  Text project = Args[ 1 ];
+  Set tolargs = Args[ 2 ];
+  Set cmdln_args = If( project=="", Copy( Empty ),
+                       [[ "-project", project ]] );
+  Set EvalSet( tolargs, Real( Set item ) {
+      Text type = Name( item[ 1 ] );
+      Case( type=="expr", {
+          Set Append( cmdln_args, [[ "-expr", item[ 1 ] ]] );
+          Real 0
+        },
+        type=="file", {
+          Real idx_rel = FindIndexByName( item, "relative" );
+          Set Append( cmdln_args,
+                      If( idx_rel, {
+                          Set [[ "-relative", item[ idx_rel ],
+                                 "-file", item[ 1 ] ]]
+                        }, {
+                          Set [[ "-file", item[ 1 ] ]]
+                        } ) );
+          Real 0
+        } ,
+        1, {
+          Warning( "RemoteTOL: tipo de elemento desconocido '"
+                   + type + "' debe "
+                   "ser 'expr' o 'file'" );
+          Real 0
+        } );
+        Real 0
+    } );
+  Set tcl_args = [[ cmd, host, port ]] << [[ cmdln_args ]];
+  //Set View( tcl_args, "Std" );
+  Set result = Tcl_EvalEx( tcl_args );
+  Real If(result["status"], Eval(result["result"]),{
+      WriteLn("RemoteTOL: "+result["result"],"E");
+      Real 0
+    })
+};
+PutDescription("Ejecuta en proceso TOL remoto. Args es un conjunto con los "
+               "argumentos a pasar a TOL en la linea de comandos.\n"
+               "Los elementos de Args son interpretados por el servidor de "
+               "procesos TOL y se traducen en argumentos de linea de comandos "
+               "al proceso TOL que se invoca. Los argumentos son:\n"
+               "\tText project = nombre del proyecto que se cargara\n"
+               "\tSet tolargs = conjunto de elementos a evaluar.\n"
+               "El conjunto tolargs contiene expresiones TOL o archivos a "
+               "evaluar en la linea de comandos. Cada uno de los "
+               "elementos es un conjunto con los siguientes elementos con "
+               "nombre:\n"
+               "\tText tolargs[1] : contiene la expresion o el nombre del "
+               "\tarchivo a evaluar. El nombre de tolargs[1] debe ser expr o "
+               "\tfile. Si el nombre es expr el valor se considera una "
+               "\texpresion TOL. Si el nombre es file el valor se considera "
+               "\tel nombre de un archivo a evaluar."
+               "\tText relative = nombre de proyecto respecto al cual se "
+               "\tcargara el archivo TOL. Este campo es opcional y solo se "
+               "\tconsidera cuando el tolargs[1] es un archivo. Por omision "
+               "\tel archivo se toma trelativo al argumento project.\n\n"
+               "Retorna el PID del proceso TOL ejecutado o 0 si algun error "
+               "ocurrio durante el intento de ejecucion. "
+               "Necesita que un servidor RmtPS_Server este escuchando en el "
+               "puerto Port del ordenador Host",
+               RemoteTOL);
+
+/**
+ *  RemoteAlive -- Verifica, en remoto, si un proceso continua vivo
+ *
+ *  Argumentos:
+ *
+ *    Host: host que aloja al servidor RMTPS
+ *    
+ *    Port: puerto donde el servidor RMPTS esta escuchando
+ *
+ *    PID: pid del proceso a verificar
+ *
+ *  Resultado:
+ *  
+ */
+Real RemoteAlive(Text Host, Real Port, Real PID)
+{
+  Set result = Tcl_Eval("::rmtps_client::ps_is_active "+Host+" "+
+                        FormatReal(Port, "%.0lf")+
+                        " " + FormatReal(PID, "%.0lf"));
+  Real If(result["status"], {
+      Real alive = Eval(result["result"]);
+      If(Eq(alive,-1), {
+          WriteLn("hubo un error en ::rmtps_server::on_ps_alive, "
+                  "rmtps_client no lo esta reportando por ahora. "
+                  "Mientras tanto puede chequear el log del servidor", "W");
+          Real 0
+        }, alive)
+    }, {
+      WriteLn("RemoteAlive: "+result["result"],"E");
+      Real 0
+    })
+};
+PutDescription("Verifica si un proceso dado por su PID continua vivo en un "
+               "ordenador remoto. "
+               "Retorna 1 si el proceso esta en la lista de procesos activos, "
+               "0 si el proceso no esta activo o -1 si hubo un error durante "
+               "la operacion remota, por ejemplo: PID invalido. "
+               "Necesita que un servidor RmtPS_Server este escuchando en el "
+               "puerto Port del ordenador Host",
+               RemoteAlive);
+
+/**
+ *  RemoteKill -- Termina la ejecucion de un proceso activo
+ *
+ *  Argumentos:
+ *
+ *    Host: host que aloja al servidor RMTPS
+ *    
+ *    Port: puerto donde el servidor RMPTS esta escuchando
+ *
+ *    PID: pid del proceso a finalizar
+ *
+ *  Resultado:
+ *  
+ */
+Real RemoteKill(Text Host, Real Port, Real PID)
+{
+  Set result = Tcl_Eval("::rmtps_client::ps_kill "+Host+" "+
+                        FormatReal(Port, "%.0lf")+
+                         " " + FormatReal(PID, "%.0lf"));
+  Real If(result["status"], {
+      Real killed = Eval(result["result"]);
+      If(Eq(killed,-1), {
+          WriteLn("hubo un error en ::rmtps_server::on_ps_kill, "
+                  "rmtps_client no lo esta reportando por ahora. "
+                  "Mientras tanto puede chequear el log del servidor","W");
+          Real 0
+        }, killed)
+    }, {
+      WriteLn("RemoteKill: "+result["result"],"E");
+      Real 0
+    })
+};
+PutDescription("Termina la ejecucion de un proceso, dado por su PID, en un "
+               "ordenador remoto. "
+               "Retorna 1 si el proceso se pudo finalizar o 0 si ocurrio un "
+               "error, por ejemplo: PID invalido, o no hay permisos "
+               "suficientes. "
+               "Necesita que un servidor RmtPS_Server este escuchando en el "
+               "puerto Port del ordenador Host",
+               RemoteKill);
+
+/**
+ *  RemoteProcessInitTcl -- Carga el codigo tcl del cliente de RmtPS_Client
+ *
+ *  Carga rmtps_client.tcl. Si durante la carga retorna con error se
+ *  retorna 0 y un mensaje de error se muestra en la salida
+ *  estandar. Si la carga fue satisfactoria se retorna 1.
+ *  
+ *  Argumentos:
+ *
+ *    path - camino del archivo rmtps_client.tcl
+ *
+ *  Resultado:
+ *
+ *    
+ */
+Real RemoteProcessInitTcl(Text path)
+{
+  Set result = Tcl_Eval("source {"+path+"}");
+  //Set View(result, "Std");
+  Real If(result["status"], 1, {
+      WriteLn("RemoteProcessInit: "+result["result"],"E");
+      Real 0
+    })
+};
+
+/*Real RemoteProcessInitTcl(GetAbsolutePath("rmtps_client.tcl"));*/
