------------------------------------------------------------------------
--
-- Módulo SGA Unix
--
------------------------------------------------------------------------

--
-- Especificação das funções públicas
--

--
-- Funções de controle do módulo.
--

------------------------------------------
--
-- Cria um novo processo e retorna o PID
--
------------------------------------------
function _create_process(p)
   if type(p) == 'string' then
      p = {cmd = p}
   end

   if not p.argv then
      local function proc_argv (x)
        x = string.gsub(x, [[([\'\"])(.*)%1]], 
          function (_, s)
            return string.gsub(s, '%s', '\\1')
          end)
        local t = {}
        string.gsub(x, '(%S+)', 
          function (s)
            s = string.gsub(s, '\\1', ' ')
            table.insert(t, s)
          end)
        return t
      end

      p.argv = proc_argv(p.cmd)
      if type(p.argv) == 'table' then
        p.cmd = p.argv[1]
      end
   elseif type(p.argv) == 'table' and not p.cmd then
      p.cmd = p.argv[1]
   end

   return unix_create_process(p)
end

------------------------------------------------------------------------------
-- Incializa o módulo para execução. Essa função deve ser chamada antes 
-- de qualquer outra função desta biblioteca.
--
-- Retorna verdadeiro caso o módulo seja inicializado com sucesso. Em caso 
-- de erro, retorna nil e a mensagem de erro.
--
-- ok, err = open()
------------------------------------------------------------------------------
open = function ()
  servermanager.name = SGAD_CONF.name           -- XXX
  servermanager.localhost = SGAD_CONF.localhost -- XXX
  PATTERNS:init()
  PROCESSES:init()

  return true
end

------------------------------------------------------------------------------
-- Termina a execução do módulo. Os processos disparados que ainda estejam
-- rodando não são afetados por essa chamada. Essa função não retorna valores.
--
-- close()
------------------------------------------------------------------------------
close = function()
  servermanager._nodes = nil
end

------------------------------------------------------------------------------
--
-- Funções de consulta à configuração de servidor.
--
------------------------------------------------------------------------------
-- Retorna o número de processadores no servidor especificado. Caso o servidor
-- seja nil, assume localhost. Em caso de erro, retorna nil e a mensagem de
-- erro.
--
-- cpus, err = getnumcpus(server)
------------------------------------------------------------------------------
getnumcpus = function (server)
   ninfo = servermanager._getnode(server)

   if not ninfo then
      return nil, servermanager.NO_SERVER
   end

   if not ninfo.numcpus or ninfo.numcpus < 0 then
     return nil,
       "Não foi possível obter o número de processadores da "..server
   end

   return ninfo.numcpus, nil
end

------------------------------------------------------------------------------
-- Retorna a ordem de bytes no servidor especificado. O retorno pode ser
-- LITTLE_ENDIAN ou BIG_ENDIAN. Caso o servidor seja nil, assume localhost.
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- order, err = getbyteorder(server)
------------------------------------------------------------------------------
getbyteorder = function (server)
   ninfo = servermanager._getnode(server)

   if not ninfo then
      return nil, servermanager.NO_SERVER
   end

   if not ninfo.byteorder or ninfo.byteorder ~= LITTLE_ENDIAN or
      ninfo.byteorder ~= BIG_ENDIAN then
      return nil, "Não foi possível obter a ordem de bytes da "..server
   end

   return ninfo.byteorder, nil
end

------------------------------------------------------------------------------
--
-- Funções de monitoração de servidor.
--
------------------------------------------------------------------------------
-- Retorna a quantidade em bytes de memória no servidor especificado.
-- O retorno é uma tabela contendo o total de memória RAM e de Swap, 
-- no formato:
--    { ram = xxx, swap = yyy }
--
-- Caso o servidor seja nil, assume localhost.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- mem, err = getmemory(server)
------------------------------------------------------------------------------
getmemory = function (server)
   ninfo = servermanager._getnode(server)

   if not ninfo then
      return nil, servermanager.NO_SERVER
   end

   if not ninfo.memory or
      not ninfo.memory.ram or ninfo.memory.ram < 0 or
      not ninfo.memory.swap or ninfo.memory.swap < 0 then
      return nil, "Não foi possível obter a memória da "..server
   end

   return ninfo.memory, nil
end

------------------------------------------------------------------------------
-- Retorna a taxa média de ocupação de CPU do último minuto no servidor
-- especificado. Esse valor considera o número de processadores que o
-- servidor possui.
--
-- Por exemplo, caso a métrica de ocupação seja o número de processos
-- na fila de prontos, este número estará dividido pela quantidade 
-- de processadores. Caso o servidor seja nil, assume localhost. 
-- 
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada 
-- no servidor em questão.
-- 
-- Em caso de erro, retorna nil e a mensagem de erro.  
--
-- load, err = getcpuload(server, maxold)
------------------------------------------------------------------------------
getcpuload = function (server, maxold)
   ninfo = servermanager._getnode(server)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end

   servermanager._update(server, maxold)

   if not ninfo.cpuload or ninfo.cpuload < 0 then
      return nil, "Falha ao obter a carga de CPU da "..server
   end

   return ninfo.cpuload, nil
end

------------------------------------------------------------------------------
-- Retorna a taxa média de ocupação de memória do último minuto no servidor
-- especificado. O retorno é uma tabela contendo o total de memória RAM e de
-- Swap, no formato:
--   { ram = xxx, swap = yyy }
--
-- Caso o servidor seja nil, assume localhost.
--
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada 
-- no servidor em questão. 
-- 
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- load, err = getmemoryload(server, maxold)
------------------------------------------------------------------------------
getmemoryload = function (server, maxold)
   ninfo = servermanager._getnode(server)

   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
  
   servermanager._update(server, maxold)

   if not ninfo or not ninfo.memoryload or
      not ninfo.memoryload.ram or ninfo.memoryload.ram < 0 or
      not ninfo.memoryload.swap or ninfo.memoryload.swap < 0 then
      return nil, "Não foi possível obter a carga de memória da "..server
   end

   return ninfo.memoryload, nil
end


----------------------------------------------------------------------
-- Retorna o número de jobs que estão em execução no servidor
-- especificado.
----------------------------------------------------------------------
getnumberofjobs = function(server, maxold)
   ninfo = servermanager._getnode(server)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   servermanager._update(server, maxold)
   if not ninfo.njobs then
     return nil, "Falha de aquisição do número de jobs!"
   end
 
   return ninfo.njobs, nil 
end


------------------------------------------------------------------------------
--
-- Funções de execução, monitoração e controle de processos.
--
------------------------------------------------------------------------------
-- Executa um comando no servidor especificado.
--
-- * Opcionalmente, pode ser passado o nome de um arquivo de onde a 
-- entrada padrão será direcionada.
-- * Opcionalmente, pode ser passado o nome de um arquivo para onde a 
-- saída padrão será direcionada. 
-- * Opcionalmente, pode ser passado o nome de um arquivo para onde a 
-- saída de erro será direcionada.
-- * A saída padrão e de erro podem ser direcionadas para o mesmo arquivo.
-- * Caso o servidor seja nil,assume localhost.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- handle, err = executecommand(identifier, command, infile, outfile,
--   errorfile, server)
------------------------------------------------------------------------------
executecommand = function(id, command, infile, outfile, errorfile, server)
   local handle ={ command = command }
   local host = server or servermanager.name
   local execute_function = "execute_command"
   if host ~= servermanager.name then
      execute_function = "execute_remote_command"
   end

   handle.host = host
   handle.time_file = os.tmpname()

   -- Recupera a função de execução apropriada que monta a string
   -- de execução do comando.
   local exec_function = PATTERNS[execute_function]
   if type(exec_function) ~= type(function() end) then
      local errmsg =  "ERRO DE CONFIGURAÇÃO DE [execute_command]."
      servermanager.writeError(errmsg)
      return nil, errmsg
   end

   -- Monta a string de execução
   local exec_string = exec_function( handle ) or "true"
   servermanager.writeCmd( "Tentando execução:\n\t"..exec_string.."\n" )

   -- Executa o comando e guarda o PID
   handle.last_update = 0
   handle.cmdid = id
   handle.pid = servermanager._create_process{
      cmd = exec_string,
      input = infile or "/dev/null",
      output = outfile or "/dev/null",
      err_output = errorfile or "/dev/null",
   }

   return handle, nil
end

------------------------------------------------------------------------------
-- Retorna uma tabela Lua com os dados do comando que devem ser persistentes.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- pdata, err = getcommandpersistentdata(handle)
------------------------------------------------------------------------------
getcommandpersistentdata = function (handle)
  if type(handle) ~= "table" or not handle.pid 
     or not tonumber(handle.pid) then
     return nil, "Handle inválido."
  end

  if ( not handle.pscommand) then
     PROCESSES:readProcessesInformation()
     local idx = PROCESSES:getIndex(handle.pid)
     if not idx then
        return nil, "Comando não encontrado."
     end
   
     handle.pscommand = PROCESSES:getCommandString(idx)
  end
  return { pid = tonumber(handle.pid),
           cmdid = handle.cmdid,
           time_file = handle.time_file,
           pscommand = handle.pscommand,
           host = handle.host,
         }, nil
end

------------------------------------------------------------------------------
-- Retorna um handle com os dados do comando que devem ser persistentes.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- handle, err = retrievecommandhandle(pdata)
------------------------------------------------------------------------------
retrievecommandhandle = function (pdata)
   if type(pdata) ~= "table" or not pdata.pid or
      not tonumber(pdata.pid) then
      return nil, "Dados inválidos."
   end
   
   PROCESSES:readProcessesInformation()
   local idx = PROCESSES:getIndex(pdata.pid)
   if idx then -- Se o processo ainda está em execução, devemos verificar
               -- se este é o processo que haviamos disparado.
     local command = PROCESSES:getCommandString(idx)
 
     if command and command ~= pdata.pscommand then  
        -- Nao posso dizer que recuperou porque existe outro processo
        -- com o mesmo pid. Apaga arquivo de tempos, mas no futuro, pode
        -- fazer retornar a notificacao e os tempos coletados...
        os.remove(pdata.time_file)
        return nil, "Comando não encontrado."
     end
   end
   
   pdata.last_update = 0 
   return pdata, nil
end

------------------------------------------------------------------------------
-- Retorna um identificador unico do processo iniciado através da função
-- executecommand.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- pid, err = getcommandpid(handle)
------------------------------------------------------------------------------
getcommandpid = function (handle)
   if type(handle) ~= "table" or not handle.pid 
      or not tonumber(handle.pid) then
      return nil, "Handle inválido."
   end

   return handle.pid, nil
end

------------------------------------------------------------------------------
-- Retorna o identificador do processo recebido na função
-- executecommand.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- id, err = getcommandid(handle)
------------------------------------------------------------------------------
getcommandid = function (handle)
   if type(handle) ~= "table" or not handle.cmdid then
      return nil, "Handle inválido."
   end

   return handle.cmdid, nil
end

------------------------------------------------------------------------------
-- Retorna o estado de um processo iniciado através da função executecommand.
--
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada no
-- servidor que estiver executando o comando.
--
-- O retorno pode ser RUNNING, NOT_RESPONDING ou FINISHED. 
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- status, err = getcommandstatus(handle, maxold)
------------------------------------------------------------------------------
getcommandstatus = function (handle, maxold)
   if type(handle) ~= "table" or
      not handle.pid or not tonumber(handle.pid) or
      not handle.last_update or not tonumber(handle.last_update) then
      return nil, "Handle inválido."
   end

   if (not maxold) or ((servermanager.now() - handle.last_update) > maxold) then
      PROCESSES:readProcessesInformation()
      handle.last_update = servermanager.now()
   end

   local idx = PROCESSES:getIndex(handle.pid)
   if idx then
     local state = PROCESSES:getState(idx)

     if state == PROCESS_RUNNING or state == PROCESS_SLEEPING
        or state == PROCESS_WAITING then
        return servermanager.RUNNING, nil
     end
   end
   -- Se o comando não aparece no 'ps' assumimos que ele terminou.
   return servermanager.FINISHED, nil
end

------------------------------------------------------------------------------
-- Retorna  a maquina que esta executando o comando definido por handle.
-- O parâmetro maxold indica o
-- tempo máximo que a informação pode ter. Caso maxold seja nil, é assumido
-- zero e a informação será buscada no servidor que estiver executando o
-- comando. Em caso de erro, retorna nil e a mensagem de erro.
--
-- host, err = getcommandexechost(handle, maxold) 
------------------------------------------------------------------------------
getcommandexechost = function (handle, maxold)
   if type(handle) ~= "table" then
      return nil, "Handle inválido."
   end

   if handle.host then
     return handle.host, nil
   end

   return nil, "host unknown"
end

------------------------------------------------------------------------------
-- Retorna a taxa média de ocupação de CPU do último minuto pelo processo
-- especificado. Esse valor considera o número de processadores que o 
-- servidor possui. Por exemplo, caso o processo execute em apenas um 
-- processador, este número estará dividido pela quantidade de processadores.
-- Caso o servidor seja nil, assume localhost.
--
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada 
-- no servidor que estiver executando o comando.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- load, err = getcommandcpuload(handle, maxold)
------------------------------------------------------------------------------
getcommandcpuload = function (handle, maxold)
   if type(handle) ~= "table" or
      not handle.pid or not tonumber(handle.pid) or
      not handle.last_update or not tonumber(handle.last_update) then
      return nil, "Handle inválido."
   end
   if (not maxold) or ((servermanager.now() - handle.last_update) > maxold) then
      PROCESSES:readProcessesInformation()
      handle.last_update = servermanager.now()
   end

   local idx = PROCESSES:getIndex(handle.pid)
   if not idx then
      return nil, "Comando não encontrado."
   end

   local cpuload = PROCESSES:getCPUPerc(idx)
   if not cpuload then
      return nil, "Não foi possível obter a carga de cpu do comando ".. 
                  handle.pid
   end

   return cpuload, nil
end

------------------------------------------------------------------------------
-- Retorna todas as informações sobre um determinado comando.
--
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada 
-- no servidor que estiver executando o comando.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- load, err = getcommandallinfo(handle, maxold)
------------------------------------------------------------------------------
getcommandallinfo = function (handle, maxold)
   if type(handle) ~= "table" or
      not handle.pid or not tonumber(handle.pid) or
      not handle.last_update or not tonumber(handle.last_update) then
      return nil, "Handle inválido."
   end

   if (not maxold) or ((servermanager.now() - handle.last_update) > maxold) then
      PROCESSES:readProcessesInformation()
      handle.last_update = servermanager.now()
   end

   local allinfo = PROCESSES:getAllInfoFromPid(handle.pid)
   if not allinfo then
      return nil, "Não foi possível obter as informações do comando "..
                  handle.pid
   end
   for i in pairs(allinfo) do                   -- PROCESSES:getAllInfoFromPid 
      allinfo[i].execHost = handle.host  -- doesn't have access to handle
   end

   return allinfo, nil
end

------------------------------------------------------------------------------
-- Retorna a taxa média de ocupação de memória do último minuto pelo processo
-- especificado. O retorno é uma tabela contendo o total de memória RAM e de
-- Swap, no formato:
--     { ram = xxx, swap = yyy }
---------------------------------
-- MAS QUAL A UNIDADE?!?! Bytes?!
---------------------------------
-- O parâmetro maxold indica o tempo máximo que a informação pode ter. Caso
-- maxold seja nil, é assumido zero e a informação será buscada no servidor
-- que estiver executando o comando. Em caso de erro, retorna nil e a mensagem
-- de erro.
--
-- load, err = getcommandmemoryload(handle, maxold)
------------------------------------------------------------------------------
getcommandmemoryload = function (handle, maxold)
   if type(handle) ~= "table" or
      not handle.pid or not tonumber(handle.pid) or
      not handle.last_update or not tonumber(handle.last_update) then
      return nil, "Handle inválido."
   end

   if (not maxold) or ((servermanager.now() - handle.last_update) > maxold) then
      PROCESSES:readProcessesInformation()
      handle.last_update = servermanager.now()
   end

   local idx = PROCESSES:getIndex(handle.pid)

   if not idx then
      return nil, "Comando não encontrado."
   end

   local mem_used = PROCESSES:getMemoryUsed(idx)
   if not mem_used or type(mem_used) ~= "table" or
      not mem_used.ram or not mem_used.swap then
      return nil, "Não foi possível obter a carga da memória "..
                  "utilizada pelo comando "..handle.pid
   end

   return mem_used, nil
end

------------------------------------------------------------------------------
-- Retorna os tempos de execução do processo especificado. O retorno é uma
-- tabela contendo os tempos de usuário, sistema e total (tempo de parede),
-- no formato:
--               { user = xxx, system = yyy, elapsed = zzz }
-- O parâmetro maxold indica o tempo máximo que a informação pode ter. Caso
-- maxold seja nil, é assumido zero e a informação será buscada no servidor
-- que estiver executando o comando.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- time, err = getcommandtimeusage(handle, maxold)
------------------------------------------------------------------------------
getcommandtimeusage = function (handle, maxold)
   local errcode = servermanager.ERROR_CODE
   if type(handle) ~= "table" or
      not handle.pid or not tonumber(handle.pid) or
      not handle.last_update or not tonumber(handle.last_update) then
      return nil, "Handle inválido."
   end

   if (not maxold) or ((servermanager.now() - handle.last_update) > maxold) then
      PROCESSES:readProcessesInformation()
      handle.last_update = servermanager.now()
   end

   local idx = PROCESSES:getIndex(handle.pid)
   if not idx then
      return servermanager._getcommandfinaltime(handle, maxold)
   end

   local state = PROCESSES:getState(idx)
   if state == PROCESS_FINISHED then
      return servermanager._getcommandfinaltime(handle, maxold)
   end

   local system = PROCESSES:getCPUTime(idx) or errcode
   local real = PROCESSES:getWallTime(idx) or errcode
   if system == errcode and real == errcode then
      return nil, "Não foi possível obter o tempo de execucao do "..
                  "comando " .. handle.pid
   end
   local info = {
     user = system,
     system = system,
     elapsed = real,
   }

   return info, nil
end


------------------------------------------------------------------------------
-- Obtém as informações dos jobs em execução no SGA --------------------------
------------------------------------------------------------------------------
getjobsinfo = function()
   return nil
end

------------------------------------------------------------------------------
-- Inicializa a variável que indica se o histórico deve ser ativado ou não
-- para o sga em questão.
------------------------------------------------------------------------------
setHistoric = function (historic)
  enableHistoric = historic
end

------------------------------------------------------------------------------
-- Retorna variável que indica se o histórico deve ser ativado ou não para o 
-- sga em questão.
--
-- enableHistoric = getHistoric()
------------------------------------------------------------------------------
getHistoric = function ()
  return enableHistoric
end

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

_getcommandfinaltime = function (handle, maxold )
  if type(handle) ~= "table" or
     not handle.pid or not tonumber(handle.pid) or
     not handle.last_update or not tonumber(handle.last_update) then
     return nil, "Handle inválido."
  end

  if maxold and handle.time_update ~= nil and
     ((servermanager.now() - handle.time_update) < maxold) then
     return handle.timeusage, nil
  end

  local func = PATTERNS.time_command_function
  local tmpfile = handle.time_file
  local str = ""

  local fd = io.open(tmpfile, "r")
  if not fd then
     servermanager.writeError( "Erro ao abrir arquivo temporário:\n"..
                              "\t["..tostring(tmpfile).."]\n"..
                              "\tpara aquisição de dados de execução\n" )
  else
    str = fd:read("*a")
    fd:close()
  end
  local real, user, system, output = func( str )
  real = tonumber(real) or errcode
  user = tonumber(user) or errcode
  system = tonumber(system) or errcode

  if string.len(output) > 0 then
    local errormsg = string.format(
      "Possível erro na execução do comando %s:\n%s",
      handle.pid, output)
    servermanager.writeError(errormsg) 
  end
  if system == errcode and real == errcode and user == errcode then
     return nil, "Não foi possível obter o tempo final de execucao do "..
                 "comando " .. handle.pid
  end

  local info = {
    user = user,
    system = system,
    elapsed = real,
  }
  handle.timeusage = info
  handle.time_update = servermanager.now()

  return info, nil
end

------------------------------------------------------------------------------
-- Interrompe um processo iniciado através da função executecommand. Retorna
-- verdadeiro caso tenha sucesso. A definição de sucesso é que uma chamada a
-- getcommandstatus para o mesmo processo retornará o valor FINISHED. Retorna
-- falso caso o processo não termine.
-- 
-- Em caso de erro, retorna nil e a mensagem de erro.
-- 
-- ok, err = killcommand(handle)
------------------------------------------------------------------------------
killcommand = function (handle)
   if type(handle) ~= "table" or
      not handle.pid or not tonumber(handle.pid) then
      return nil, "Handle inválido."
   end

   local idx = PROCESSES:getIndex(handle.pid)
   if not idx then
      return nil, "Comando não encontrado."
   end
   local idx_children = PROCESSES:getIndexesFromChildren(idx)

   kill_process( handle.pid )
   for i,idx in ipairs(idx_children) do
      local cpid = PROCESSES:getProcessId(idx)
      if cpid then
         kill_process(cpid)
      end
   end

   return 1
end

------------------------------------------------------------------------------
-- Libera todos os recursos relacionados ao processo especificado. Esta função
-- precisa ser chamada após o término do processo para que eventuais recursos
-- alocados a ele possam ser liberados. Após a chamada desta função, o handle
-- do processo não poderá mais ser utilizado.
--
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- ok, err = releasecommandinfo(handle)
------------------------------------------------------------------------------
releasecommandinfo = function (handle)
   os.remove(handle.time_file)
   return 1
end

------------------------------------------------------------------------------
--
-- Especificação das funções privadas
--
------------------------------------------------------------------------------
dofile("lib/Unix-patterns.lua")
dofile("lib/Unix-processes.lua")

_getnode = function(server)
   server = server or servermanager.name
   local ninfo = servermanager._nodes[server]
   if not ninfo then
      ninfo = servermanager._add(server)
   end
   return ninfo
end

_add = function(nodename)
   local errcode = servermanager.ERROR_CODE
   if not servermanager._nodes[nodename] then
      local new_node = {}
---
      local FUNCTION = type( function() end )
      local STRING = type("")
      local NUMBER = type(0)
      local TABLE = type({})
      local sfunc = PATTERNS.get_node_static_data
      local numcpus = 1
      local memory = {errcode, errcode}
      local byteorder = servermanager.LITTLE_ENDIAN

      if type(sfunc) == FUNCTION then
         numcpus, memory, byteorder = sfunc( nodename)
         if type(numcpus) ~= NUMBER or numcpus < 1 then 
            numcpus = 1
            servermanager.writeError( "Falha de aquisição de número de cpus!")
         end
         if type(memory) ~= TABLE or #memory ~= 2 or
            type(memory[1]) ~= NUMBER or type(memory[2]) ~= NUMBER or
            memory[1] < 0 or memory[2] < 0 then
            memory = {errcode, errcode}
            servermanager.writeError( "Falha de aquisição de memória!")
         end
         if type(byteorder) ~= STRING or
            (byteorder ~= "LITTLE_ENDIAN" and byteorder ~= "BIG_ENDIAN") then
            byteorder = servermanager.LITTLE_ENDIAN
            servermanager.writeError( "Falha de aquisição de byteorder!")
         else
            if byteorder == "LITTLE_ENDIAN" then
               byteorder = servermanager.LITTLE_ENDIAN
            else
               byteorder = servermanager.BIG_ENDIAN
            end
         end
      else
         servermanager.writeError( "Falha de aquisição de dados estáticos.")
      end

      new_node.numcpus = numcpus
      new_node.memory = { ram = memory[1], swap = memory[2]}
      new_node.byteorder = byteorder
      new_node.last_update = 0
---
      servermanager._nodes[nodename] = new_node
   end

   return servermanager._nodes[nodename]
end

------------------------------------------------------------------------------
-- _update(server, maxload)
-- O parâmetro
-- maxold indica o tempo máximo que a informação pode ter. Caso maxold seja
-- nil, é assumido zero e a informação será buscada no servidor em questão.
--
-- Campos estáticos do nó:
-- numcpus
-- memory
--          { ram = xxx, swap = yyy }
-- byteorder
--          LITTLE_ENDIAN ou BIG_ENDIAN.
--
-- Campos dinâmicos do nó:
-- cpuload
-- memoryload
--          { ram = xxx, swap = yyy }
------------------------------------------------------------------------------
_update = function(node, freq)
   local nodeinfo = servermanager._nodes[node]
   if freq and (servermanager.now() - nodeinfo.last_update) < freq then
      return nodeinfo
   end

   nodeinfo.last_update = servermanager.now()
   local FUNCTION = type( function() end )
   local NUMBER = type(0)
   local TABLE = type({})
   local dfunc = PATTERNS.get_node_dynamic_data
   local mload, load_perc = {errcode, errcode} , {errcode, errcode, errcode}

   if type(dfunc) == FUNCTION then
      mload, load_perc = dfunc( node)
      if type(mload) ~= TABLE or #mload ~= 2 or
         type(mload[1]) ~= NUMBER or mload[1] < 0 or
         type(mload[2]) ~= NUMBER or mload[2] < 0 then
        servermanager.writeError( "Falha de aquisição de carga da memória!")
        mload = {errcode, errcode}
      end
      if type(load_perc) ~= TABLE or #load_perc ~= 3 or
         type(load_perc[1]) ~= NUMBER or load_perc[1] < 0 or
         type(load_perc[2]) ~= NUMBER or load_perc[2] < 0 or
         type(load_perc[3]) ~= NUMBER or load_perc[3] < 0 then
        load_perc = {errcode, errcode, errcode}
        servermanager.writeError( "Falha de aquisição de carga de CPU!")
      end
   end

   -- número de jobs = número total de comandos em execução
   local njobs = 0
   for i, v in pairs(COMMAND.array) do
      njobs = njobs + 1
   end

   nodeinfo.cpuload = load_perc[1]
   nodeinfo.memoryload = {ram = mload[1], swap = mload[2]}
   nodeinfo.njobs = njobs

   return nodeinfo
end

writeMsg = function(str)
  local msg = "SGAD ["..os.date("%d/%m/%y - %H:%M:%S").."] - "..str
  print(msg)
  io.flush()
end
writeError = writeMsg
writeCmd = writeMsg

------------------------------------------------------------------------
servermanager = {
  -------------
  -- private --
  -------------
  NO_SERVER= "Servidor não existente", -- Colocar num arquivo de mensagens?
  _nodes   = {},
  _add     = _add,
  _getcommandfinaltime  = _getcommandfinaltime,
  _getnode = _getnode,
  _update  = _update,
  _create_process = _create_process,
  -------------
  -- public --
  -------------
  now = now,
  sleep = sleep,
  writeMsg = writeMsg,
  writeError = writeError,
  writeCmd = writeCmd,

  -- Funções de controle do módulo:
  open = open,
  close = close,

  -- Funções de consulta à configuração de servidor:
  getnumcpus = getnumcpus,
  getmemory = getmemory,
  getbyteorder = getbyteorder,

  -- Funções de monitoração de servidor:
  getcpuload = getcpuload,
  getmemoryload = getmemoryload,
  getnumberofjobs = getnumberofjobs,

  -- Funções de execução, monitoração e controle de processos:
  executecommand = executecommand,
  retrievecommandhandle = retrievecommandhandle,       -- EXTRA
  getcommandpersistentdata = getcommandpersistentdata, -- EXTRA
  getcommandallinfo = getcommandallinfo,               -- EXTRA
  getcommandid = getcommandid,
  getcommandpid = getcommandpid,
  getcommandstatus = getcommandstatus,
  getcommandexechost = getcommandexechost,
  getcommandcpuload = getcommandcpuload,
  getcommandmemoryload = getcommandmemoryload,
  getcommandtimeusage = getcommandtimeusage,
  getjobsinfo = getjobsinfo,
  killcommand = killcommand,
  releasecommandinfo = releasecommandinfo,
  
  -- Funções de log de histórico
  setHistoric = setHistoric,
  getHistoric = getHistoric,

  -- Constantes do módulo:
  RUNNING = 0,
  NOT_RESPONDING = 1,
  WAITING = 2,
  FINISHED = 3,

  LITTLE_ENDIAN = 0,
  BIG_ENDIAN = 1,

  ERROR_CODE = -1
}

