---------------------------------------------------------------------
-- $Author$
-- $Revision$  - $Date$
---------------------------------------------------------------------
local verbose = require "sga.verbose"
local ext = require "shellExtension"
---------------------------------------------------------------------
-- Tabelas globais
---------------------------------------------------------------------
COMMAND = {}
COMMAND.array = {}
COMMAND.end_table = {}

-- Possiveis estados de um comando
COMMAND.state = {
   RUNNING  = "RUNNING",
   SLEEPING = "SLEEPING",
   WAITING  = "WAITING",
   FINISHED = "FINISHED",
}

COMMAND.ERROR_CODE = -1

----------------------------------------------------------------------
-- States (idl):
--    "RUNNING"
--    "SLEEPING"
--    "WAITING"
--    "FINISHED"
--
-- States (lib):
--    RUNNING
--    WAITING
--    NOT_RESPONDING
--    FINISHED
---------------------------------------------------------------------

---------------------------------------------------------------------
-- Retorna o estado do comando
---------------------------------------------------------------------
function COMMAND:getState(state)
   if state == SGAD_CONF.serverManager.RUNNING then
      return COMMAND.state.RUNNING
   elseif state == SGAD_CONF.serverManager.WAITING then
      return COMMAND.state.WAITING
   elseif state == SGAD_CONF.serverManager.NOT_RESPONDING then
      return COMMAND.state.RUNNING
   else --if state == SGAD_CONF.serverManager.FINISHED then
      return COMMAND.state.FINISHED
   end
end

---------------------------------------------------------------------
-- Adiciona um comando a lista de comandos
---------------------------------------------------------------------
function COMMAND:addCommandToList(cmd)
   if cmd and cmd.id then
     self.array[cmd.id] = cmd 
     self:writePersistentData()
   end
end

---------------------------------------------------------------------
-- Envia notificação de término de comando para o servidor
---------------------------------------------------------------------
function COMMAND:SendCompletedInfo( cmd )
   local info
   local ret, err = SGAD_CONF.serverManager.getcommandtimeusage(cmd.handle, 
                                      SGAD_CONF.completed_time_seconds)
   if err then
      verbose.warning(string.format("ERRO: %s\n", err))
      info = {
        elapsedTimeSec = self.ERROR_CODE,
        userTimeSec = self.ERROR_CODE,
        cpuTimeSec = self.ERROR_CODE,
      }
   else
      info = {
        elapsedTimeSec = ret.elapsed,
        userTimeSec = ret.user,
        cpuTimeSec = ret.system,
      }
   end

   -- envia notificação ao servidor
   local ssi = SGA_DAEMON.ssi
   -- A versão atual do luaorb não está tratando as exceções de
   -- comunicação com o servidor. Utilizar pcall para evitar
   -- a parada do SGA.
   -- local stat = ssi:commandCompleted(SGA_DAEMON:getHostName(), 
   --                                   cmd, cmd.id, info, cmd.command )
   local suc, stat = scheduler.pcall(ssi.commandCompleted, ssi, 
               SGA_DAEMON:getHostName(), cmd, 
               cmd.id, info, cmd.command )
   -- Se ocorreu erro, tenta enviar novamente a notificação.
   if not suc then
      verbose.error("Falha no envio de notificação de fim de comando")
      return nil
   end
   -- Se o servidor rejeitou a notificação, não adianta enviar novamente.
   if not stat then
      verbose.error("Falha na notificação de fim de comando")
   end

   verbose.cmdcomp("Tempo decorrido ("..tostring(cmd.id).."): "..
                       tostring(info.elapsedTimeSec)..", ".. 
                       tostring(info.userTimeSec)..", "..
                       tostring(info.cpuTimeSec) )

   verbose.cmdcomp("Desativando o servant CORBA!")
   cmd:deactivate()
  
   return true
end

---------------------------------------------------------------------
-- Persiste a tabela de comandos
---------------------------------------------------------------------
function COMMAND:writePersistentData()
   local ptable = {}

   for id, cmd in pairs(self.array) do
      if cmd and type(cmd) == type({}) then 
         local server = SGAD_CONF.serverManager
         local libdata, err = server.getcommandpersistentdata(cmd.handle)
         if err then
            verbose.error(string.format("ERRO: %s\n", err))
         else
            local pdata = {
               command = cmd.command,
               path = cmd.path,
               output_path = cmd.output_path,
               id = cmd.id,
               host = cmd.host,
               libdata = libdata,
			   sandbox_paths = cmd.sandbox_paths,
            }
            table.insert(ptable, pdata)
         end
      else
         verbose.error( "\t"..tostring(id).." - NULO!" )
      end
   end

   local succeeded, fd = pcall(io.open, SGAD_CONF.dump_file, "w")
   if not succeeded or not fd then
      verbose.error(string.format("ERRO: o arquivo de persistência %s não pode ser aberto.\n", SGAD_CONF.dump_file))
      if (fd) then
        -- fd neste caso é a mensagem de erro
        verbose.error(fd.."\n")
      end
      return
   end
   local succeeded, err = pcall(fd.write, fd, "return ".. COMMAND:tostring(ptable))
   if not succeeded then
      verbose.error("ERRO: nao foi possivel gravar o arquivo de persistência.\n")
      verbose.error(err.."\n")
      return
   end
   succeeded, err = pcall(fd.close, fd)
   if not succeeded then
      verbose.error("ERRO: nao foi possivel fechar o arquivo de persistência.\n")
      verbose.error(err.."\n")
      return
   end
end

---------------------------------------------------------------------
-- Carrega os dados dos comandos
---------------------------------------------------------------------
function COMMAND:loadPersistentData()
   -- Carrega o arquivo de persistência de comandos em execução
   local func = loadfile(SGAD_CONF.dump_file)
   local ptable

   -- Executa o arquivo carregado se não houve erro
   if func then
      ptable = func()
   end

   -- Arquivo inválido, comandos perdidos
   if type(ptable) ~= type({}) then 
      verbose.warning("Erro lendo arquivo de persistencia")
      ptable = {}
   end

   self.end_table = {}
   local nc = #ptable

   for c = 1, nc do
      local pdata = ptable[c]
      local cmd = {}

      if pdata and type(pdata) == type({}) then 
         local server = SGAD_CONF.serverManager
         local handle, err = server.retrievecommandhandle(pdata.libdata)
         if err then
            verbose.warning(string.format("ERRO: %s\n", err))
            if CSFS.launch_daemon == YES and pdata.id == CSFS.process_id then
               CSFS.process = nil   
            elseif SGAD_CONF.net_benchmark == YES and pdata.id == IPERF.process_id then
               IPERF.process = nil   
            else
               table.insert( self.end_table, pdata )
            end
         else
            setmetatable( cmd, CommandClass.meta )
            cmd.parent = CommandClass
            cmd.command = pdata.command
            cmd.path = pdata.path
            cmd.output_path = pdata.output_path
            cmd.id = pdata.id
            cmd.host = pdata.host
            cmd.handle = handle
            cmd.expired = SGAD_CONF.process_time_seconds
            cmd.main_pid, err = SGAD_CONF.serverManager.getcommandpid(handle)
			cmd.sandbox_paths = pdata.sandbox_paths
            if err then
               cmd.main_pid = 0
            end
            verbose.cmdretr( "Comando recuperado:\n"..
                 "\tComando: ["..cmd.command.."]\n"..
                 "\tId: ["..cmd.id.."]\n"..
                 "\tHost: ["..tostring(cmd.host).."]\n" ..
                 "\tPID: ["..tostring(cmd.main_pid).."]." )
            self:addCommandToList(cmd)
            if CSFS.launch_daemon == YES and pdata.id == CSFS.process_id then
               CSFS.process = cmd
            elseif SGAD_CONF.net_benchmark == YES and pdata.id == IPERF.process_id then
               IPERF.process = cmd
            end
         end
      else
         verbose.error( "\t"..tostring(c).." - NULO!" )
      end
   end
   self:notifyRetrievedItems()
end

---------------------------------------------------------------------
-- Notifica o servidor com os comandos recuperados 
---------------------------------------------------------------------
function COMMAND:notifyRetrievedItems()

   -- Envia notificação de termino de comandos que estavam no arquivo
   -- de persistencia, mas nao foram recuperados.
   for i = 1, #self.end_table do
      local ssi = SGA_DAEMON.ssi
      local end_cmd = self.end_table[i]
      if end_cmd and type(end_cmd) == type({}) then
        verbose.cmdretr(
           "Comando não recuperado. Enviando notificação - Id: " ..
           tostring(end_cmd.id) )
        -- A versão atual do luaorb não está tratando as exceções de
        -- comunicação com o servidor. Utilizar pcall para evitar
        -- a parada do SGA.
        -- local stat = ssi:commandLost( SGA_DAEMON:getHostName(), 
        --                               end_cmd.id, end_cmd.command )
        local suc, stat = scheduler.pcall(ssi.commandLost, ssi, 
                 SGA_DAEMON:getHostName(), 
                 end_cmd.id, end_cmd.command )
        if not suc or not stat then
          verbose.error(
               "Falha no envio de notificação de comando não recuperado.")
          return
        end
      end
   end
   
   local rtable = {}

   for id, cmd in pairs(self.array) do
      if cmd and type(cmd) == type({}) then 
        -- Não enviar notificação de recuperação ao servidor
        -- se o comando for o CSFS.
        if ((not (CSFS.launch_daemon == YES and id == CSFS.process_id)) and
            (not (SGAD_CONF.net_benchmark == YES and id == IPERF.process_id))) then
            table.insert(rtable, {cmdId = cmd.id, cmdRef = cmd} )
        end
      else
        verbose.error( "\t"..tostring(id).." - NULO!" )
      end
   end
   -- Envia comandos recuperados
   local ssi = SGA_DAEMON.ssi
   -- A versão atual do luaorb não está tratando as exceções de
   -- comunicação com o servidor. Utilizar pcall para evitar 
   -- a parada do SGA.
   -- local stat = ssi:commandRetrieved( SGA_DAEMON:getHostName(), rtable)
   local suc, stat = scheduler.pcall(ssi.commandRetrieved, ssi, 
               SGA_DAEMON:getHostName(), rtable)
   if not suc or not stat then
      verbose.error("Falha no envio de comando recuperado.")
      return
   end
end

---------------------------------------------------------------------
-- Procura por comandos finalizados e notifica o servidor
---------------------------------------------------------------------
function COMMAND:searchCompletedItems()
   -- Acumulamos os comandos terminados
   -- e enviamos as mensagens para o SSI de uma vez!
   local end_table = {}
   for id, cmd in pairs(self.array) do
      if cmd and type(cmd) == type({}) then 
         local server = SGAD_CONF.serverManager
         local handle = cmd.handle
         local pid, err = server.getcommandpid(handle)
         if err then
            verbose.warning(string.format("ERRO: %s\n", err))
            pid = 0
         end
         local state, err = 
            server.getcommandstatus(handle, SGAD_CONF.completed_time_seconds)
         if err then
            verbose.warning(string.format("ERRO: %s\n", err))
            state = server.NOT_RESPONDING
         end
         if state == server.FINISHED then
            verbose.cmdcomp("\t"..tostring(id)..
                    " - Terminado: "..tostring(pid))
            if CSFS.launch_daemon == YES and cmd.id == CSFS.process_id then
               verbose.cmdcomp("Desativando o servant CORBA!")
               cmd:deactivate()
               CSFS.process = nil 
               if CSFS.retries_number then
                 if CSFS.retries_number > 0 then 
                   local retryNumber = (SGAD_CONF.csfs_retries_number - CSFS.retries_number) + 1
                   verbose.cmdcomp("Reiniciando CSFS: "..retryNumber.." de "..SGAD_CONF.csfs_retries_number.." tentativas")               
                   CSFS.retries_number = CSFS.retries_number - 1
                 else 
                   verbose.kill("Não foi possível iniciar o CSFS.")
                   SGA_DAEMON:exit(0)
                 end
               end 
               SGA_DAEMON:createCSFSProcess()
            elseif SGAD_CONF.net_benchmark == YES and cmd.id == IPERF.process_id then
               verbose.cmdcomp("Desativando o servant CORBA!")
               cmd:deactivate()
               IPERF.process = nil 
               SGA_DAEMON:createIPerfProcess()
            else
               table.insert(end_table, cmd)
            end
         else
            -- Não exibe mensagem quando o comando é o CSFS ou IPERF
            if cmd.id == CSFS.process_id then
              CSFS.retries_number = SGAD_CONF.csfs_retries_number
            elseif cmd.id ~= IPERF.process_id then
              verbose.cmdcomp("\t"..tostring(id).." - Rodando: "..tostring(pid))
            end
         end
      else
         verbose.cmdcomp( "\t"..tostring(id).." - NULO!" )
      end
   end

  -- Envia as notificações 
   for i = 1, #end_table do
      local end_cmd = end_table[i]

      if end_cmd and type(end_cmd) == type({}) then
        verbose.cmdcomp("Notificação de término ao SSI - pid: "..
                 tostring(end_cmd:getPid()))
        if not self:SendCompletedInfo(end_cmd) then
          verbose.error("Erro ao notificar o ssi do final do comando "..end_cmd.id)
        end
      end
   end
   return true
end

---------------------------------------------------------------------
-- Serializa os elementos de Lua em uma string
---------------------------------------------------------------------
function COMMAND:tostring(val)
   local t = type(val)
   
   if t == "table" then
      local f, s = next(val, nil)
      local str = '{'
      while f do
         if not tonumber(f) then
            str = str .. "[\"" .. f .. "\"]=" .. self:tostring(s)
         else
            str = str .. "["   .. f .. "]="   .. self:tostring(s)
         end
     str = str .. ","

         f, s = next(val, f)
      end
      return str .. '}'
   elseif t == "string" then
          return "[[" .. val .. "]]"
   elseif t == "number" then
          return val
   else -- if not tab then
          return "nil"
   end
end


---------------------------------------------------------------------
---------------------------------------------------------------------
---------------------------------------------------------------------

---------------------------------------------------------------------
-- Função que define o mecanismo de herança com o campo 'parent'
---------------------------------------------------------------------
function inherit_from_parent( tab, index )
   if type(tab) ~= type({}) then
      error( "indexed expression is not a table.")
   end

   local parent = rawget(tab, "parent")
   if parent then
      return parent[ index ]
   else
      return nil
   end
end


----------------------------------------------------------------------
-- Tabela que implementa a interface SGACommand
----------------------------------------------------------------------
CommandClass = {}
CommandClass.meta = {
   __index = inherit_from_parent
}

----------------------------------------------------------------------
-- Executa o comando na máquina selecionada
----------------------------------------------------------------------
function Command( sga_command )
   if not sga_command.command then 
      verbose.error( "Comando nulo abortado!" )
      return nil 
   end

   -----------------------------------------------------------
   -- Configura a meta table para disponibilizar as funções --
   -- de gerenciamento de comandos                          --
   -----------------------------------------------------------
   setmetatable(sga_command, CommandClass.meta)
   sga_command.parent = CommandClass
   -----------------------------------------------------------

   sga_command.command = tostring( sga_command.command )
   if not sga_command.id then 
      verbose.error( "Comando sem identificador abortado!" )
      return nil 
   end
   sga_command.id = tostring( sga_command.id )
   -----------
   -- START --
   -----------
   local start= ext.createDirectoryChooser()
   start:add(sga_command.path)    -- diretório de Binários do Algoritmo
   start:add(SGAD_CONF.ssibindir) -- diretório de Binários do Framework
   start:add(SGAD_CONF.sgabindir) -- diretório do Binário do SGA
   local startdir = start:choose("start")

   if startdir then
     local id = sga_command.id
     -- Preciso colocar os parâmetros numa variável por causa do ";".
     local paramcmd = string.gsub(sga_command.command,"([;<>\(\)])","\\%1")
     local sandbox = sga_command:getSandboxDir()
     local startcmd = [["%sstart" %s "%s"; %s]]
     sga_command.command = string.format(startcmd,startdir,id,sandbox,paramcmd)
   end
   ---------
   -- Verifica se a maquina escolhida existe ou escolhe uma maquina.
   local host
   if sga_command.host then
      host = string.upper(sga_command.host)
   end
   local output
   local err_output
   if host then
      if host ~= string.upper(SGAD_CONF.name) then
         local nodes = SGAD_CONF.nodes
         local found = false

         for k,tab in ipairs(nodes) do
            if string.upper(tab.name) == host then
               found = tab.name
               break
            end
         end

         if not found or not SGAD_CONF.network[ host ] then
            local errmsg = "No configuration found for host: "..sga_command.host
            verbose.error( "\n\n"..errmsg.."!!!\n\n")
            return nil, errmsg
         end
         host = found
      else
         host = nil
      end
   elseif SGA_DAEMON:isCluster() and not SGAD_CONF.deny_node_selection then
      local nodes = SGA_DAEMON:getNodesDynamicInfo()
      local minAvg = nodes[1].load_avg_perc.loadAvg1min
      host = nodes[1].name

      for i,v in pairs(nodes) do
         if type(v) == "table" and v.load_avg_perc.loadAvg1min >= 0 and
            v.load_avg_perc.loadAvg1min < minAvg then
            minAvg = v.load_avg_perc.loadAvg1min
            host = v.name
         end
      end
   end
   sga_command.host = host or SGAD_CONF.name

   if sga_command.sandbox_paths then
      -- Cria sandboxes de execução do comando
      for i = 1, #sga_command.sandbox_paths do
         local sandbox = sga_command:getSandboxDir(i)
         verbose.debug("Criando diretório de sandbox "..sandbox)
         if ext.isDir(sandbox) then
	   	 	verbose.error("Diretório de sandbox "..sandbox.." do comando "..sga_command.id.." já existe! ")
	  	 else
	        if not ext.mkdirs(sandbox) then
	     		verbose.error("Erro ao criar o diretório de sandbox "..sandbox.." do comando "..sga_command.id)
	    		return nil
			end
	  	 end
      end
   else 
      if ((not (CSFS.launch_daemon == YES and sga_command.id == CSFS.process_id)) and
         (not (SGAD_CONF.net_benchmark == YES and sga_command.id == IPERF.process_id))) then
	     verbose.error("Sandbox de execução do comando "..sga_command.id.." não está disponível! ")
	  end
   end
   
   -- Escolheu uma maquina para a execucao do comando...
   local handle, err = 
      SGAD_CONF.serverManager.executecommand(sga_command.id,
        sga_command.command, nil, output,err_output, host, startdir,
        sga_command.output_path)
   if err then
      verbose.error(string.format("Erro ao executar comando [%s]: %s\n",
              sga_command.command, err))
      return nil
   else
      sga_command.main_pid, err = SGAD_CONF.serverManager.getcommandpid(handle)
      if err then
         sga_command.main_pid = 0
      end

      sga_command.handle = handle
      sga_command.expired = SGAD_CONF.process_time_seconds
      verbose.cmdexec("Comando para execução:\n"..
              "\tComando: ["..sga_command.command.."]\n"..
              "\tId: ["..sga_command.id.."]\n"..
              "\tHost: ["..tostring(sga_command.host).."]\n" ..
              "\tPID: ["..tostring(sga_command.main_pid).."]." )
   end
   return sga_command
end

----------------------------------------------------------------------
-- Retorna o caminho completo para o diretório da sandbox de execução 
-- do comando para um determinado índice ou retorna o caminho para a 
-- primeira sandbox se não for passado nenhum índice
----------------------------------------------------------------------
function CommandClass:getSandboxDir(index)
     return SGAD_CONF.sandbox_root_directory..SGAD_CONF.file_separator..self.sandbox_paths[index or 1]
end

----------------------------------------------------------------------
-- Define valores padrão para as informações de um processo.
----------------------------------------------------------------------
function CommandClass:setDefaults(defaults)
  self.defaults = defaults
end

----------------------------------------------------------------------
-- Preenche as informações de um processo com valores padrão.
----------------------------------------------------------------------
function CommandClass:fillDefaults(tab)
  for field, value in pairs(self.defaults) do
     if not tab[field] then
       tab[field] = value
     end
  end
end

----------------------------------------------------------------------
-- Retorna as informações sobre o status do comando 
----------------------------------------------------------------------
function CommandClass:parseCommandInfo(filename)
  local tab = {}
  local attrs = {}
  local nitems = 0 -- Faz o papel do getn, que não funciona neste caso.
  local insertInfo = function()
    if nitems < 1 then
      return
    end
    self:fillDefaults(attrs)
    local validState = false
    for i, v in pairs(COMMAND.state) do
      if attrs.state == v then
        validState = true
        break
      end
    end
    if not validState then
      attrs.state = COMMAND.state.FINISHED
    end
    table.insert(tab, attrs)
    attrs = {}
    nitems = 0
  end
  for line in io.lines(filename) do
    local emptyline = true
    for i, v in string.gmatch(line, "(%w+)%s*=%s*([%.%w]+)") do
      attrs[i] = tonumber(v) or v
      emptyline = false
      nitems = nitems + 1
    end
    if emptyline then
      insertInfo()
    end
  end
  insertInfo()
  return tab 
end

----------------------------------------------------------------------
-- Retorna o PID do comando em execução
----------------------------------------------------------------------
function CommandClass:getPid()
   return self.main_pid
end

----------------------------------------------------------------------
-- Retorna o estado de execução do comando
----------------------------------------------------------------------
function CommandClass:isRunning()
   local server = SGAD_CONF.serverManager
   return server.getcommandstatus(self.handle, self.expired) == server.RUNNING
end

function CommandClass:isWaiting()
   local server = SGAD_CONF.serverManager
   return server.getcommandstatus(self.handle, self.expired) == server.WAITING
end

----------------------------------------------------------------------
-- Retorna as informações sobre o comando
----------------------------------------------------------------------
function CommandClass:getRunningCommandInfo()
  local info = self:collectProcessInfo()
  local extra = self:collectExecutionData()
  return { processData = info,
           executionData = extra
         }
end

----------------------------------------------------------------------
-- Coleta informações adicionais sobre o comando em execução.
----------------------------------------------------------------------
function CommandClass:collectExecutionData()
   local attrs = {}
   local status= ext.createDirectoryChooser()
   status:add(self.path)           -- diretório de Binários do Algoritmo
   status:add(SGAD_CONF.ssibindir) -- diretório de Binários do Framework
   status:add(SGAD_CONF.sgabindir) -- diretório do Binário do SGA
   local statusdir = status:choose("collect_execution_data")
   if statusdir then
     local id = self.id or self.main_pid
     --local outputfile = os.tmpname()
     local outputfile = ".tmpFile."..SGA_DAEMON:getName()..id
     local sandbox = self:getSandboxDir()
     ext.execCmdLine(ext.SHELL_SH, "'"..statusdir.."collect_execution_data'", outputfile, false, id, "'"..sandbox.."'")
     for line in io.lines(outputfile) do
       for key, value in string.gmatch(line, "(.-)%s*=%s*([^,]+)") do
         table.insert(attrs, { key = key, value = value })
       end
     end
     os.remove(outputfile)
   end
   return attrs
end

----------------------------------------------------------------------
-- Coleta os dados do comando em execução.
----------------------------------------------------------------------
function CommandClass:collectProcessInfo()
   local infodefault = {
       pid = self.main_pid,
       ppid = 0,
       command = self.command,
       execHost = "unknown",
       state = COMMAND.state.FINISHED,
       processorId = 0,
       memoryRamSizeMb = COMMAND.ERROR_CODE,
       memorySwapSizeMb = COMMAND.ERROR_CODE,
       CPUPerc = COMMAND.ERROR_CODE,
       CPUTimeSec = COMMAND.ERROR_CODE,
       wallTimeSec = COMMAND.ERROR_CODE,
       userTimeSec = COMMAND.ERROR_CODE,
       systemTimeSec = COMMAND.ERROR_CODE,
       virtualMemorySizeMB = COMMAND.ERROR_CODE,
       bytesInKB = COMMAND.ERROR_CODE,
       bytesOutKB = COMMAND.ERROR_CODE,
       diskBytesReadKB = COMMAND.ERROR_CODE,
       diskBytesWriteKB = COMMAND.ERROR_CODE,
   }
   self:setDefaults(infodefault)
   ------------
   -- STATUS --
   ------------
   local status= ext.createDirectoryChooser()
   status:add(self.path)           -- diretório de Binários do Algoritmo
   status:add(SGAD_CONF.ssibindir) -- diretório de Binários do Framework
   status:add(SGAD_CONF.sgabindir) -- diretório do Binário do SGA
   local statusdir = status:choose("status")
   if statusdir then
     local id = SGAD_CONF.serverManager.getcommandid(self.handle) or self.main_pid
     --local output = os.tmpname()
     local output = ".tmpFile."..SGA_DAEMON:getName()..id
     local sandbox = self:getSandboxDir()
     ext.execCmdLine(ext.SHELL_SH, "'"..statusdir.."status'", output, false, id, "'"..sandbox.."'")
     local tab = self:parseCommandInfo(output)
     os.remove(output)
     if #tab == 0 then
       tab = { infodefault}
     end
     return tab
   end
   ---------
   if ( not self:isRunning() ) and ( not self:isWaiting() ) then -- XXX Precisa verificar?
      local server = SGAD_CONF.serverManager
      local status = server.getcommandstatus(self.handle, self.expired)
      local ehost = server.getcommandexechost(self.handle, self.expired)
      verbose.warning("PID não encontrado:\n" .. "\t[" ..self.command ..
              "] "..tostring(self.main_pid) .."." )
      infodefault.execHost = ehost or infodefault.execHost
      if status then
        infodefault.state = COMMAND:getState(status)
      end
      return { infodefault }
   end

   local server = SGAD_CONF.serverManager
   local ehost,err = server.getcommandexechost(self.handle, self.expired)
   if err then
      verbose.warning(string.format("ERRO: %s\n", err))
      ehost = "unknown"
   end
   if not server.getcommandallinfo then
      local status, err = server.getcommandstatus(self.handle, self.expired)
      if err then
         verbose.warning(string.format("ERRO: %s\n", err))
         status = server.NOT_RESPONDING
      end

      local memorySize, err = 
         server.getcommandmemoryload(self.handle, self.expired)
      if err then
         verbose.warning(string.format("ERRO: %s\n", err))
         memorySize = {
           ram = COMMAND.ERROR_CODE,
           swap = COMMAND.ERROR_CODE
         }
      end

      local CPUPerc, err = server.getcommandcpuload(self.handle, self.expired)
      if err then
         verbose.warning(string.format("ERRO: %s\n", err))
         CPUPerc = COMMAND.ERROR_CODE
      end

      local tinfo, err = server.getcommandtimeusage(self.handle, self.expired)
      if err then
         verbose.warning(string.format("ERRO: %s\n", err))
         tinfo = { 
            system = COMMAND.ERROR_CODE,
            elapsed = COMMAND.ERROR_CODE,
            user = COMMAND.ERROR_CODE
         }
      end
      local CPUTimeSec = tinfo.system
      local wallTimeSec = tinfo.elapsed
      
      -- A idl diz que um comando pode ter um ou mais processos, então
      -- returnar uma tabela de tabelas como resultado
      return {{
        pid = self.main_pid,
        ppid = 0,
        command = self.command,
        execHost = ehost,
        state = COMMAND:getState(status),
        processorId = 0,
        memoryRamSizeMb = memorySize.ram  / (1024 * 1024),
        memorySwapSizeMb = memorySize.swap  / (1024 * 1024),
        CPUPerc = CPUPerc,
        CPUTimeSec = CPUTimeSec,
        wallTimeSec = wallTimeSec,
        userTimeSec = COMMAND.ERROR_CODE,
        systemTimeSec = COMMAND.ERROR_CODE,
        virtualMemorySizeMB = COMMAND.ERROR_CODE,
        bytesInKB = COMMAND.ERROR_CODE,
        bytesOutKB = COMMAND.ERROR_CODE,
        diskBytesReadKB = COMMAND.ERROR_CODE,
        diskBytesWriteKB = COMMAND.ERROR_CODE,
      }}
   else
      local allinfo, err = server.getcommandallinfo(self.handle, self.expired)
      if err then
        verbose.warning(string.format("ERRO: %s\n", err))
        allinfo = { infodefault }
      else
        for i = 1, #allinfo do
          self:fillDefaults(allinfo[i])
        end
      end
      return allinfo
   end
end

----------------------------------------------------------------------
-- Retorna um referência para o SGA
----------------------------------------------------------------------
function CommandClass:getSGAServer()
   return SGA_DAEMON.servant
end

----------------------------------------------------------------------
-- Termina a execução do comando
----------------------------------------------------------------------
function CommandClass:kill()
   verbose.debug("Cancelando o comando "..self.id)
   ----------
   -- STOP --
   ----------
   local stop= ext.createDirectoryChooser()
   stop:add(self.path)           -- diretório de Binários do Algoritmo
   stop:add(SGAD_CONF.ssibindir) -- diretório de Binários do Framework
   stop:add(SGAD_CONF.sgabindir) -- diretório do Binário do SGA
   local stopdir = stop:choose("stop")
   if stopdir then
     local id = SGAD_CONF.serverManager.getcommandid(self.handle) or
       self.main_pid
     local sandbox = self:getSandboxDir()
     ext.execCmdLine(ext.SHELL_SH, "'"..stopdir.."stop'", nil, false, id, "'"..sandbox.."'")
   else
   verbose.debug("Chama api específica "..self.id)
     local ok, err = SGAD_CONF.serverManager.killcommand(self.handle)
   verbose.debug("Volta api específica "..self.id)
     if err then
        verbose.error(string.format("ERRO: %s", err))
     end
   end
   ---------
   verbose.cmdkill("Desativando o servant CORBA para comando abortado: "..self.id)
   verbose.debug("Chama deactivate "..self.id)
   self:deactivate()
   verbose.debug("Terminou deactivate "..self.id)
end

----------------------------------------------------------------------
-- Desativa o servant CORBA.
-- Necessário para liberar a referência Lua
----------------------------------------------------------------------
function CommandClass:deactivate()
   --local lo_servant = self._last_lo_servant
   --lo_servant:_deactivate() -- IMPORTANTE VOLTAR!

   verbose.debug("Vai atualizar a persistência "..self.id)
   COMMAND.array[self.id] = nil
   COMMAND:writePersistentData()
   if self.sandbox_paths then
      for i = 1, #self.sandbox_paths do
         local sandbox = self:getSandboxDir(i)
         verbose.debug("Removendo diretório de sandbox "..sandbox)
         if not ext.isDir(sandbox) then
            verbose.warning("Diretório de sandbox "..sandbox.." do comando "..self.id.." não encontrado (pode já ter sido removido pelo comando).")
         else 
            if not ext.rmdir(sandbox) then
               verbose.error("Erro ao remover de sandbox "..sandbox.." do comando "..self.id)
      	    end
         end 
      end
   end
   verbose.debug("Atualizou a persistência "..self.id)
   SGAD_CONF.serverManager.releasecommandinfo(self.handle)
   verbose.debug("Liberou área do comando "..self.id)
end

----------------------------------------------------------------------
-- Imprime toda a tabela
----------------------------------------------------------------------
function _dump(tb) 
   for i,cmd_tb in ipairs(tb) do
      for idx,val in pairs(cmd_tb) do
         print(idx,val)
      end
   end
end

