/******************************************************************
 *
 * Título: SgaLib
 *   Tipo: Módulo de Implementação
 *
 * Autor: Eduardo Melo Pacheco
 * Data: 04/11/2003
 *
 * Descrição: Módulo que implementa funções para utilizar serviços
 *            do sistema operacional Windows, nas versões NT 4, 2K
 *            e XP. Estes serviços são de obtenção de dados de
 *            hardware, monitoramento de desempenho, disparo
 *            de processos e monitoramento e gerenciamento dos mesmos.
 *            As funções são exportadas para LUA 5 e encapsuladas na
 *            tabela global servermanager. O módulo não disponibiliza
 *            acesso à nenhuma função fora de um contexto LUA 5.
 *
 ******************************************************************/

#include <windows.h>
#include <map>
#include <list>

extern "C"
{
   #include <lua.h>
   #include <lualib.h>
   #include <lauxlib.h>
}

using namespace std;

#define TOTALBYTES     100*1024  // Tamanho Inicial do Buffer para dados
                                 // de desempenho
#define BYTEINCREMENT  10*1024   // Incremento do Buffer para dados de desempenho
#define NS_TO_S        10000000  // Fator de conversão da unidade de timer
                                 // do Windows

// Objetos do WMI
#define SYSTEM_OBJECT_INDEX     2        // 'System' object
#define PROCESSOR_OBJECT_INDEX  238      // 'Processor' object
#define PROCESS_OBJECT_INDEX    230      // 'Process' object
#define DISK_OBJECT_INDEX		234		 // 'Disk' object
#define NETWORK_OBJECT_INDEX	510	     // 'Network' object

// Contadores do WMI
#define TOTAL_PROCESSOR_TIME_COUNTER_INDEX 240  // '% Total processor time' counter (Para WinNT no 'System' object)
#define PROCESSOR_TIME_COUNTER_INDEX       6    // '% processor time' counter (para Win2K/XP)
#define PROCESS_PID_COUNTER_INDEX          784  // PID
#define PROCESS_PPID_COUNTER_INDEX         1410 // Creating Process ID
#define PROCESS_WORKINGSET_COUNTER_INDEX   180  // Proces's Working Set em Bytes
#define ELAPSED_TIME_COUNTER_INDEX         684  // Elapsed Time counter
#define PROCESS_SWAPMEMORY_COUNTER_INDEX   184  // Virtual Bytes Counter
#define PROCESS_USERTIME_COUNTER_INDEX     142  // % User Time
#define PROCESS_KERNELTIME_COUNTER_INDEX   144  // % Privileged Time
#define PROCESS_VIRTUALBYTES_COUNTER_INDEX 174  // Process virtual memory
#define DISK_READBYTESSEC_COUNTER_INDEX      	220  // Disk read rate per second
#define DISK_WRITEBYTESSEC_COUNTER_INDEX     	222  // Disk write rate per second
#define NETWORK_BYTESRECVSEC_COUNTER_INDEX   	264  // Network bytes received per second
#define NETWORK_BYTESSENTSEC_COUNTER_INDEX   	506  // Network bytes sent per second 

#define RUNNING        0
#define NOT_RESPONDING 1
#define WAITING		   2
#define FINISHED       3

#define LITTLE_ENDIAN  0
#define BIG_ENDIAN     1

// Estrutura de dados que contém as versões do Windows
// para determinar qual contador pegar.
typedef enum
{
   WINNT,   WIN2K_XP, WIN9X, UNKNOWN
} PLATFORM ;

// Estrutura que guarda tempos de processos, utilizados
// para cálculo de uso de CPU.
typedef struct _PROCESSTIMES PROCESSTIMES, * PPROCESSTIMES;

struct _PROCESSTIMES
{
   ULARGE_INTEGER execTime ;
   LARGE_INTEGER eTime ;
} ;

// Estrutura para guardar handles de processos, necessários
// para obter dados dos comandos disparados. A cada handle
// é associado um PID para busca.
typedef struct _COMMAND COMMAND , * PCOMMAND ;

struct _COMMAND
{
    DWORD pid ;
    LPCTSTR CommandId ;
    HANDLE hName ;
    HANDLE hProcess ;
    HANDLE hStdIn ;
    HANDLE hStdOut ;
    HANDLE hStdError ;
} ;

// Estrutura de dados para guardar dados referentes à processos.
typedef struct _PROCESS
{
  DWORD PID ;
  DWORD PPID ;
  double MemoryUsed ;
  double SwapMemoryUsed ;
  double CPU_PERC;
  double KernelTime ;
  double UserTime ;
  double WallTime ;
  double VirtualBytes ;
  double DiskBytesRead ;
  double DiskBytesWrite ;
  double BytesIn ;
  double BytesOut ;
}  PROCESS , *PPROCESS ;

// Estrutura de dados para guardar dados referentes a E/S.
typedef struct _IO
{
  double DiskBytesRead ;
  double DiskBytesWrite ;
  double BytesIn ;
  double BytesOut ;
}  IODATA;

// Tipos definidos para facilitar a manipulação dos índices 
typedef map<DWORD,double> MapCPU ;
typedef map<DWORD,unsigned long> MapProcess ;
typedef multimap<DWORD,unsigned long> MapChildren ;
typedef list<DWORD> ListProcess ;

typedef pair<DWORD,unsigned long> Process_Pair ;
typedef pair<DWORD,double> CPU_Pair;


typedef struct _PROCESSCONTROL
{
   double TimeStamp ; // Time stamp da útlima vez em que os dados de processo foram atualizados
   unsigned long NumProcesses ;
   PPROCESS aProcessData ;
   MapChildren children ;  // Guarda o mapeamento dos filhos de um processo.
   MapProcess index ;      // Facilita a busca a um processo.
   MapCPU cpu1 ;       // cpu1 e cpu2 guardam os valores dos percentuais 
   MapCPU cpu2 ;       // de CPU antigos e atuais.
   MapCPU *oldCPU ;    // oldCPU e currCPU apontam para cpu1 e cpu2 alternadamente 
   MapCPU *currCPU ;   // (evita gerencia de memória).
}  PROCESSCONTROL ;

// Variável global para guardar controle de atualização e
// dados de processos
static PROCESSCONTROL Processes ;

// Armazena o número de CPUs da máquina
static DWORD numberOfCpus = 0 ;

// Armazena os últimos dados de E/S coletados
IODATA IOData;

// Variável global que indica se histórico deve ser ativado
BOOLEAN enableHistoric;

/******************************************************************
 *
 *   Função GetPlatform:
 *    Função utilizada para descobrir a versão do Windows.
 *
 *   Parâmetros:
 *      A função não recebe parâmetros.
 *
 *   Valor de retorno:
 *      Tipo: Platform
 *      Descrição: Contém a versão do sistema operacional.
 *
 ******************************************************************/

static PLATFORM GetPlatform()
{
   // Estrutura que contém dados sobre a versão
   OSVERSIONINFO osvi ;
   osvi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );

   // Chamada à função da API para descobrir a versão
   if ( !GetVersionEx( &osvi ) )
      return UNKNOWN;
   switch ( osvi.dwPlatformId )
   {
   case VER_PLATFORM_WIN32_WINDOWS:
      return WIN9X ;
   case VER_PLATFORM_WIN32_NT :
      if ( osvi.dwMajorVersion == 4)
         return WINNT ;
      else
         return WIN2K_XP ;
   }
   return UNKNOWN ;
}

/*****************************************************************
 *
 * Funções utilizadas para navegar pelos dados retornados pelo WMI
 *
 *****************************************************************/

/******************************************************************
 *
 *   Função FirstObject:
 *    Função utilizada para saber o endereço do primeiro objeto
 *      retornado pela consulta ao WMI, que retorna um bloco de dados.
 *
 *   Parâmetros:
 *   - PerfData:
 *      Tipo: PPERF_DATA_BLOCK definido na API do Windows.
 *      Descrição: Bloco de dados retornado pela consulta aos
 *               dados de performance do WMI.
 *
 *   Valor de retorno:
 *      Tipo: PPERF_DATA_BLOCK definido na API do Windows.
 *      Descrição: Ponteiro para o primeiro objeto retornado no bloco
 *                 de dados de performance do WMI.
 *
 ******************************************************************/


static PPERF_OBJECT_TYPE FirstObject( PPERF_DATA_BLOCK PerfData )
{
    return( (PPERF_OBJECT_TYPE) ( ( PBYTE ) PerfData +
        PerfData->HeaderLength ) ) ;
}

/******************************************************************
 *
 *   Função NextObject:
 *    Função utilizada para saber o endereço do próximo objeto,
 *      dado um objeto no bloco de dados retornado pela consulta
 *    ao WMI,
 *
 *   Parâmetros:
 *   - PerfObj:
 *      Tipo: PPERF_OBJECT_TYPE, definido na API do Windows.
 *      Descrição: Objeto de performance retornado no bloco de
 *               dados de performance do WMI.
 *
 *   Valor de retorno:
 *      Tipo: PPERF_OBJECT_TYPE, definido na API do Windows.
 *      Descrição: Próximo objeto de performance retornado no
 *                 bloco de dados de performance do WMI.
 *
 ******************************************************************/

static PPERF_OBJECT_TYPE NextObject( PPERF_OBJECT_TYPE PerfObj )
{
    return( ( PPERF_OBJECT_TYPE )( ( PBYTE ) PerfObj +
        PerfObj->TotalByteLength ) ) ;
}


/******************************************************************
 *
 *   Função FirstInstance:
 *    Função utilizada para saber o endereço da primeira instância
 *      de um dado objeto do WMI.
 *
 *   Parâmetros:
 *   - PerfObj:
 *      Tipo: PPERF_OBJECT_TYPE, definido na API do Windows.
 *      Descrição: Objeto de performance retornado no bloco de
 *               dados de performance do WMI.
 *
 *   Valor de retorno:
 *      Tipo: PPERF_INSTANCE_DEFINITION,
 *           definido na API do Windows.
 *      Descrição: Primeira instância de um dado objeto retornado
 *                 no bloco de dados de performance do WMI.
 *
 ******************************************************************/

static PPERF_INSTANCE_DEFINITION FirstInstance( PPERF_OBJECT_TYPE PerfObj )
{
    return( ( PPERF_INSTANCE_DEFINITION )( ( PBYTE )PerfObj +
        PerfObj->DefinitionLength ) ) ;
}

/******************************************************************
 *
 *   Função NextInstance:
 *    Função utilizada para saber o endereço da próxima instância,
 *   dada uma instância de um dado objeto do WMI.
 *
 *   Parâmetros:
 *   - PerfInst:
 *      Tipo: PPERF_INSTANCE_DEFINITION,
 *            definido na API do Windows.
 *      Descrição: Instância de um dado objeto retornado
 *               no bloco de dados de performance do WMI.
 *
 *   Valor de retorno:
 *      Tipo: PPERF_INSTANCE_DEFINITION,
 *            definido na API do Windows.
 *      Descrição: Próxima instância de uma dado Objeto do WMI
 *
 ******************************************************************/

static PPERF_INSTANCE_DEFINITION NextInstance( PPERF_INSTANCE_DEFINITION PerfInst )
{
    PPERF_COUNTER_BLOCK PerfCntrBlk;

    PerfCntrBlk = ( PPERF_COUNTER_BLOCK ) ( ( PBYTE ) PerfInst +
        PerfInst->ByteLength ) ;

    return( ( PPERF_INSTANCE_DEFINITION )( ( PBYTE ) PerfCntrBlk +
        PerfCntrBlk->ByteLength ) ) ;
}

/******************************************************************
 *
 *   Função FirstCounter:
 *    Função utilizada para saber o endereço do primeiro contador
 *      de um dado objeto do WMI.
 *
 *   Parâmetros:
 *   - PerfObj:
 *      Tipo: PPERF_OBJECT_TYPE, definido na API do Windows.
 *      Descrição: Objeto de performance retornado no bloco de
 *                 dados de performance do WMI.
 *
 *   Valor de retorno:
 *      Tipo: PPERF_COUNTER_DEFINITION, definido na API do Windows.
 *      Descrição: Primeiro contador de um dado objeto retornado
 *               no bloco de dados de performance do WMI.
 *
 ******************************************************************/

static PPERF_COUNTER_DEFINITION FirstCounter( PPERF_OBJECT_TYPE PerfObj )
{
    return( ( PPERF_COUNTER_DEFINITION ) ( ( PBYTE ) PerfObj +
        PerfObj->HeaderLength ) ) ;
}

/******************************************************************
 *
 *   Função NextCounter:
 *    Função utilizada para saber o endereço do próximo contador,
 *      dado um contador de um dado objeto do WMI.
 *
 *   Parâmetros:
 *   - PerfCntr:
 *      Tipo: PPERF_COUNTER_DEFINITION,
 *            definido na API do Windows.
 *      Descrição: Contador de performance retornado em um
 *                 objeto no bloco de dados de performance do WMI.
 *
 *   Valor de retorno:
 *      Tipo: PPERF_COUNTER_DEFINITION,
 *            definido na API do Windows.
 *      Descrição: Próximo contador de um dado objeto retornado
 *               no bloco de dados de performance do WMI.
 *
 ******************************************************************/

static PPERF_COUNTER_DEFINITION NextCounter( PPERF_COUNTER_DEFINITION PerfCntr )
{
    return( ( PPERF_COUNTER_DEFINITION )( ( PBYTE )PerfCntr +
        PerfCntr->ByteLength ) ) ;
}

/******************************************************************
 *
 *   Função UnicodeToASCII:
 *    Função utilizada para converter strings com codificação
 *    UNICODE para codificação ASCII.
 *
 *   Parâmetros:
 *   - UString:
 *      Tipo: wchar_t *
 *      Descrição: Vetor de wchar_t, que contém a string
 *                 com codificação Unicode a ser convertida.
 *   - ASCIIString:
 *      Tipo: char *
 *      Descrição: Vetor de char, que receberá a string convertida
 *                 para codificação ASCII.
 *
 *   Valor de retorno:
 *      A função não retorna nada.
 *
 ******************************************************************/

static void UnicodeToASCII ( const wchar_t * UString , char * ASCIIString )
{
   unsigned int StringSize = wcslen( UString ) ;
   unsigned int i ;

   for ( i = 0 ; i < StringSize ; i++  )
   {
      ASCIIString[ i ] = ( char ) __toascii( UString[ i ] ) ;
   }
   ASCIIString[ StringSize ] = '\0' ;
   return ;
}

/******************************************************************
 *
 *   Função QueryPerfomanceData:
 *  Função utilizada para alocar espaço em memória e receber
 *  dados de performance da máquina local utilizando a interface
 *  de registro do WMI
 *
 *   Parâmetros:
 *
 *
 *   Valor de retorno:
 *      Tipo: PPERF_DATA_BLOCK
 *      Descrição: Bloco de dados de performance do WMI
 *
 ******************************************************************/

static PPERF_DATA_BLOCK QueryPerformanceData ( )
{
   DWORD BufferSize = TOTALBYTES ;
   DWORD cbData;
   DWORD dwRet;
   char objects[255];

   // Aloca memória para receber dados de performance do WMI.
   PPERF_DATA_BLOCK PerfData = ( PPERF_DATA_BLOCK ) malloc( BufferSize ) ;
   if (PerfData == NULL) {
      return NULL;
   }
   cbData = BufferSize;

   sprintf(objects, "%d %d %d %d %d\0", SYSTEM_OBJECT_INDEX,
      PROCESSOR_OBJECT_INDEX, PROCESS_OBJECT_INDEX, DISK_OBJECT_INDEX,
      NETWORK_OBJECT_INDEX);
   dwRet = RegQueryValueEx( HKEY_PERFORMANCE_DATA, // Chave do registro referente a dados de performance
      objects,           // Objetos a serem consultados
      NULL,
      NULL,
      (LPBYTE) PerfData, // Ponteiro que reberá os dados
      &cbData );
   while( dwRet == ERROR_MORE_DATA ) {
      // Aumentar o tamanho do buffer até que seja grande o suficiente
      BufferSize += BYTEINCREMENT ;
      PerfData = ( PPERF_DATA_BLOCK ) realloc ( PerfData, BufferSize ) ;
      if (PerfData == NULL) {
         return NULL;
      }
      cbData = BufferSize;
      dwRet = RegQueryValueEx( HKEY_PERFORMANCE_DATA, // Chave do registro referente a dados de performance
         objects,           // Objetos a serem consultados
         NULL,
         NULL,
         (LPBYTE) PerfData, // Ponteiro que reberá os dados
         &cbData );
   }
   if( dwRet != ERROR_SUCCESS ) {
      free(PerfData);
      return NULL;
   }
   return PerfData ;
}

/*****************************************************************
 *
 * Funções utilizadas para pegar valores de contadores do WMI
 *
 *****************************************************************/

/******************************************************************
 *
 *   Função GetObjectFromIndex
 *    Função utilizada para recuperar o endereço de um determinado
 *  objeto dentro de um bloco de performance retornado pelo WMI.
 *
 *   Parâmetros:
 *  - pPerfData:
 *    Tipo: PPERF_DATA_BLOCK
 *    Descrição: Bloco de dados de performance retornado pelo WMI.
 *  - dwObjectIndex:
 *    Tipo: DWORD, redefinição unsigned long.
 *    Descrição: Índice do objeto de performance no bloco de
 *               performance retornado pelo WMI
 *
 *   Valor de retorno:
 *      Tipo: PPERF_OBJECT_TYPE
 *      Descrição: Ponteiro para o objeto de índice passado como
 *                 parâmetro.
 *
 ******************************************************************/

static PPERF_OBJECT_TYPE GetObjectFromIndex ( PPERF_DATA_BLOCK pPerfData , unsigned long dwObjectIndex )
{
   PPERF_OBJECT_TYPE pPerfObj = NULL;

   // Primeiro objeto
   pPerfObj = FirstObject( pPerfData );

   // Procura pelo objeto de índice recebido como parâmetro
   for( unsigned long i=0; i < pPerfData->NumObjectTypes; i++ )
   {

      if (pPerfObj->ObjectNameTitleIndex == dwObjectIndex)
      {
         return pPerfObj;
      }

      pPerfObj = NextObject( pPerfObj );
   }
   return NULL;
}

/******************************************************************
 *
 *   Função GetULLCounterValueFromObject:
 *    Função utilizada para recuperar o valor de um contador de
 *    um determinado objeto de performance do WMI. Há a de se
 *    passar uma instância como parâmetro.
 *
 *
 *   Parâmetros:
 *  - pPerfObj:
 *    Tipo: PPERF_OBJECT_TYPE
 *    Descrição: Objeto contendo contadores de performance do WMI.
 *  - dwCounterIndex:
 *    Tipo: DWORD, redefinição unsigned long.
 *    Descrição: Índice do contador no objeto de performance.
 *  - pInstanceName:
 *    Tipo:LPCTSTR, string
 *    Descrição: String contendo o nome da instância do objeto da
 *               qual se deseja recuperar o dado. Se não houverem
 *               instâncias no objeto, o valor do contador no
 *               objeto é retornardo.
 *
 *   Valor de retorno:
 *      Tipo: ULONGLONG
 *      Descrição: Valor contendo o contador desejado no objeto
 *                 recebido como parâmetro.
 *
 ******************************************************************/

static ULONGLONG GetULLCounterValueFromObject( PPERF_OBJECT_TYPE pPerfObj ,
                                               unsigned long dwCounterIndex ,
                                               char * szInstanceName )
{
   PPERF_COUNTER_DEFINITION pPerfCntr = NULL ;
   PPERF_INSTANCE_DEFINITION pPerfInst = NULL ;
   PPERF_COUNTER_BLOCK pCounterBlock = NULL ;

   if ( pPerfObj != NULL )
   {

      // Descobre referência para o primeiro contador do objeto
      pPerfCntr = FirstCounter( pPerfObj ) ;

      // Procura pelo índice recebido como parâmetro
      for( unsigned long j=0; j < pPerfObj->NumCounters; j++ )
      {
         if (pPerfCntr->CounterNameTitleIndex == dwCounterIndex)
            break ;

         // Vai para o próximo contador
         pPerfCntr = NextCounter( pPerfCntr ) ;
      }

      if( pPerfObj->NumInstances == PERF_NO_INSTANCES )
      {
         pCounterBlock = ( PPERF_COUNTER_BLOCK ) ( ( LPBYTE ) pPerfObj + pPerfObj->DefinitionLength ) ;
      }
      else
      {
         pPerfInst = FirstInstance( pPerfObj );

         // Procura pela instância do objeto recebido como parâmetro
         char szInstance[256] ;
         char szInputInstance[256] ;
         strncpy(   szInputInstance , szInstanceName, 255 ) ;
         for( int k=0 ; k < pPerfObj->NumInstances ; k++ )
         {
            UnicodeToASCII( ( wchar_t * ) ( ( PBYTE )pPerfInst + pPerfInst->NameOffset ) , szInstance ) ;
            if (!strcmp( szInstance, szInputInstance ) )
            {
               pCounterBlock = ( PPERF_COUNTER_BLOCK ) ( ( LPBYTE ) pPerfInst + pPerfInst->ByteLength ) ;
               break;
            }

            // Próxima instância.

            pPerfInst = NextInstance( pPerfInst ) ;
         }
      }

      if ( pCounterBlock )
      {
         ULONGLONG * lnValue  ;
         lnValue = ( ULONGLONG * )( ( LPBYTE ) pCounterBlock + pPerfCntr->CounterOffset ) ;
         return * lnValue ;
      }
      return 0 ;
   }
   else
   {
      return 0 ;
   }
}

/******************************************************************
 *
 *   Função GetCounterValueFromInstance:
 *    Função utilizada para recuperar o valor de um contador de
 *  uma determinada instância de um objeto de performance do WMI.
 *
 *   Parâmetros:
 *  - pPerfObj:
 *    Tipo: PPERF_OBJECT_TYPE
 *    Descrição: Objeto contendo contadores de performance do WMI.
 *  - dwCounterIndex:
 *    Tipo: DWORD, redefinição unsigned long.
 *    Descrição: Índice do contador no objeto de performance.
 *  - pPerfInst:
 *    Tipo:PPERF_INSTANCE_DEFINITION
 *    Descrição: Instância do objeto do qual se deseja recuperar
 *               o valor do contador.
 *
 *   Valor de retorno:
 *      Tipo: DWORD
 *      Descrição: Valor contendo o contador desejado no objeto
 *                 recebido como parâmetro.
 *
 ******************************************************************/

static DWORD GetCounterValueFromInstance ( PPERF_OBJECT_TYPE pPerfObj, unsigned long dwCounterIndex, PPERF_INSTANCE_DEFINITION pPerfInst )
{
   PPERF_COUNTER_DEFINITION pPerfCntr = NULL ;
   PPERF_COUNTER_BLOCK pCounterBlock = NULL ;


   pPerfCntr = FirstCounter( pPerfObj ) ;

   for( unsigned long j=0 ; j < pPerfObj->NumCounters ; j++ )
   {
      if (pPerfCntr->CounterNameTitleIndex == dwCounterIndex)
         break ;

      // Pega o próximo contador.
      pPerfCntr = NextCounter( pPerfCntr ) ;
   }

   pCounterBlock = ( PPERF_COUNTER_BLOCK ) ( ( LPBYTE ) pPerfInst + pPerfInst->ByteLength ) ;

   DWORD * lnValue ;
   lnValue = ( DWORD * )( ( LPBYTE ) pCounterBlock + pPerfCntr->CounterOffset ) ;
   return ( *lnValue ) ;
}

/******************************************************************
 *
 *   Função GetLLCounterValueFromInstance:
 *    Função utilizada para recuperar o valor de um contador de
 *  uma determinada instância de um objeto de performance do WMI.
 *
 *   Parâmetros:
 *  - pPerfObj:
 *    Tipo: PPERF_OBJECT_TYPE
 *    Descrição: Objeto contendo contadores de performance do WMI.
 *  - dwCounterIndex:
 *    Tipo: DWORD, redefinição unsigned long.
 *    Descrição: Índice do contador no objeto de performance.
 *  - pPerfInst:
 *    Tipo:PPERF_INSTANCE_DEFINITION
 *    Descrição: Instância do objeto do qual se deseja recuperar
 *               o valor do contador.
 *
 *   Valor de retorno:
 *      Tipo: LONGLONG
 *      Descrição: Valor contendo o contador desejado no objeto
 *                 recebido como parâmetro.
 *
 ******************************************************************/

static LONGLONG GetLLCounterValueFromInstance ( PPERF_OBJECT_TYPE pPerfObj, unsigned long dwCounterIndex, PPERF_INSTANCE_DEFINITION pPerfInst )
{
   PPERF_COUNTER_DEFINITION pPerfCntr = NULL ;
   PPERF_COUNTER_BLOCK pCounterBlock = NULL ;

   pPerfCntr = FirstCounter( pPerfObj ) ;

   for( unsigned long j=0 ; j < pPerfObj->NumCounters ; j++ )
   {
      if ( pPerfCntr->CounterNameTitleIndex == dwCounterIndex)
         break ;

      // Próximo contados.
      pPerfCntr = NextCounter( pPerfCntr ) ;
   }

   pCounterBlock = ( PPERF_COUNTER_BLOCK ) ( ( LPBYTE ) pPerfInst + pPerfInst->ByteLength ) ;

   LONGLONG * lnValue ;
   lnValue = ( LONGLONG * )( ( LPBYTE ) pCounterBlock + pPerfCntr->CounterOffset ) ;
   return ( * lnValue ) ;
}

/******************************************************************
 *
 *   Função GetCounterValueFromObject:
 *    Função utilizada para recuperar o valor de um contador de
 *  um determinado objeto de performance do WMI. Há a
 *  de se passar uma instância como parâmetro.
 *
 *
 *   Parâmetros:
 *  - pPerfObj:
 *    Tipo: PPERF_OBJECT_TYPE
 *    Descrição: Objeto contendo contadores de performance do WMI.
 *  - dwCounterIndex:
 *    Tipo: DWORD, redefinição unsigned long.
 *    Descrição: Índice do contador no objeto de performance.
 *  - pInstanceName:
 *    Tipo:LPCTSTR, string
 *    Descrição: String contendo o nome da instância do objeto da
 *               qual se deseja recuperar o dado. Se não houverem
 *               instâncias no objeto, o valor do contador no
 *               objeto é retornardo.
 *
 *   Valor de retorno:
 *      Tipo: DWORD
 *      Descrição: Valor contendo o contador desejado no objeto
 *                 recebido como parâmetro.
 *
 ******************************************************************/

static DWORD GetCounterValueFromObject( PPERF_OBJECT_TYPE pPerfObj , unsigned long dwCounterIndex , char * szInstanceName )
{
   PPERF_COUNTER_DEFINITION pPerfCntr = NULL ;
   PPERF_INSTANCE_DEFINITION pPerfInst = NULL ;
   PPERF_COUNTER_BLOCK pCounterBlock = NULL ;

   // Descobre referência para o primeiro contador do objeto
   pPerfCntr = FirstCounter( pPerfObj ) ;

   // Procura pelo índice recebido como parâmetro
   for( unsigned long j=0; j < pPerfObj->NumCounters; j++ )
   {
      if (pPerfCntr->CounterNameTitleIndex == dwCounterIndex)
         break ;

      // Vai para o próximo contador
      pPerfCntr = NextCounter( pPerfCntr ) ;
   }

   if( pPerfObj->NumInstances == PERF_NO_INSTANCES )
   {
      pCounterBlock = ( PPERF_COUNTER_BLOCK ) ( ( LPBYTE ) pPerfObj + pPerfObj->DefinitionLength ) ;
   }
   else
   {
      pPerfInst = FirstInstance( pPerfObj );

      // Procura pela instância do objeto recebido como parâmetro
      char szInstance[256] ;
      char szInputInstance[256] ;
      strncpy(   szInputInstance , szInstanceName, 255 ) ;
      for( int k=0 ; k < pPerfObj->NumInstances ; k++ )
      {
         UnicodeToASCII( ( wchar_t * ) ( ( PBYTE )pPerfInst + pPerfInst->NameOffset ) , szInstance ) ;
         if (!strcmp( szInstance, szInputInstance ) )
         {
            pCounterBlock = ( PPERF_COUNTER_BLOCK ) ( ( LPBYTE ) pPerfInst + pPerfInst->ByteLength ) ;
            break;
         }

         // Próxima instância.
         pPerfInst = NextInstance( pPerfInst ) ;
      }
   }

   if ( pCounterBlock )
   {
      DWORD * lnValue ;
      lnValue = ( DWORD * )( ( LPBYTE ) pCounterBlock + pPerfCntr->CounterOffset ) ;
      return ( *lnValue ) ;
   }
   return 0 ;
}

/******************************************************************
 *
 *   Função GetCounterValue:
 *    Função utilizada para recuperar o valor de um contador de
 *  uma determinada instância, se especificada, de um objeto cujo
 *  índice é passado como parâmetro.
 *
 *   Parâmetros:
 *  - pPerfObj:
 *    Tipo: PPERF_DATA_BLOCK
 *    Descrição: Objeto contendo contadores de performance do WMI.
 *  - dwObjectIndex:
 *    Tipo: DWORD, redefinição unsigned long.
 *    Descrição: Índice do objeto de performance no bloco de
 *               performance retornado pelo WMI
 *  - dwCounterIndex:
 *    Tipo: DWORD, redefinição unsigned long.
 *    Descrição: Índice do contador no objeto de performance.
 *  - pInstanceName:
 *    Tipo:LPCTSTR, string
 *    Descrição: String contendo nome da instância do objeto do
 *               qual se deseja recuperar o valor do contador.
 *               Caso o objeto não possua instâncias, o contador
 *               no objeto será retornado.
 *
 *   Valor de retorno:
 *      Tipo: DWORD
 *      Descrição: Valor contendo o contador desejado no objeto
 *                 recebido como parâmetro.
 *
 ******************************************************************/

static DWORD GetCounterValue( PPERF_DATA_BLOCK pPerfData, unsigned long dwObjectIndex, unsigned long dwCounterIndex, char * pInstanceName )
{
   PPERF_OBJECT_TYPE pPerfObj = NULL ;
   DWORD lnValue = 0 ;

   // Primeiro Objeto
   pPerfObj = FirstObject( pPerfData ) ;

   // Procuro pelo objeto recebido como parâmetro
   for( unsigned long i=0 ; i < pPerfData->NumObjectTypes ; i++ )
   {

      if ( pPerfObj->ObjectNameTitleIndex == dwObjectIndex )
      {
         lnValue = GetCounterValueFromObject( pPerfObj, dwCounterIndex, pInstanceName ) ;
         break;
      }

      pPerfObj = NextObject( pPerfObj ) ;
   }
   return lnValue ;
}

/******************************************************************
 *
 *   Função GetULLCounterValue:
 *    Função utilizada para recuperar o valor de um contador de
 *  uma determinada instância, se especificada, de um objeto cujo
 *  índice é passado como parâmetro.
 *
 *   Parâmetros:
 *  - pPerfObj:
 *    Tipo: PPERF_DATA_BLOCK
 *    Descrição: Objeto contendo contadores de performance do WMI.
 *  - dwObjectIndex:
 *    Tipo: DWORD, redefinição unsigned long.
 *    Descrição: Índice do objeto de performance no bloco de
 *               performance retornado pelo WMI
 *  - dwCounterIndex:
 *    Tipo: DWORD, redefinição unsigned long.
 *    Descrição: Índice do contador no objeto de performance.
 *  - pInstanceName:
 *    Tipo:LPCTSTR, string
 *    Descrição: String contendo nome da instância do objeto do
 *               qual se deseja recuperar o valor do contador.
 *               Caso o objeto não possua instâncias, o contador
 *               no objeto será retornado.
 *
 *   Valor de retorno:
 *      Tipo: ULONGLONG
 *      Descrição: Valor contendo o contador desejado no objeto
 *                 recebido como parâmetro.
 *
 ******************************************************************/

static ULONGLONG GetULLCounterValue( PPERF_DATA_BLOCK pPerfData,
                              unsigned long dwObjectIndex,
                              unsigned long dwCounterIndex,
                              char * pInstanceName )
{
   PPERF_OBJECT_TYPE pPerfObj = NULL ;
   ULONGLONG lnValue = 0 ;

   // Primeiro Objeto
   pPerfObj = FirstObject( pPerfData ) ;

   // Procuro pelo objeto recebido como parâmetro

   for( unsigned long i=0 ; i < pPerfData->NumObjectTypes ; i++ )
   {

      if ( pPerfObj->ObjectNameTitleIndex == dwObjectIndex )
      {
         lnValue = GetULLCounterValueFromObject( pPerfObj, dwCounterIndex, pInstanceName ) ;
         break;
      }

      pPerfObj = NextObject( pPerfObj ) ;
   }
   return lnValue ;
}

/*****************************************************************
 *
 * Funções de abertura e fechamento do módulo, CHAMADAS de LUA.
 *
 *****************************************************************/

static int _open( lua_State * L )
{
   lua_pushnumber( L , 1 ) ;
   lua_pushnil( L ) ;
   return 2 ;
}

static int _close( lua_State * L )
{
   RegCloseKey( HKEY_PERFORMANCE_DATA ) ;
   return 0 ;
}

/*****************************************************************
 *
 * Funções utilizadas para pegar dados de Hardware da máquina.
 *
 *****************************************************************/

/******************************************************************
 *
 *   Função _getStaticMemoryInfo
 *    Função utilizada para recuperar a quantidade de memória física
 *    instalada e o tamanho da memória de Swap.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    uma tabela na qual o campo ram é o valor de memória física
 *    instalada e o campo swap é o tamanho de memória swap. Também
 *    é inserido na pilha o valor nil, que é a descrição do erro.
 *    A API do Windows não retorna erros para esta função.
 *
 ******************************************************************/

static int _getStaticMemoryInfo( lua_State * L )
{
   // Estrutura que receberá os dados de memória
   MEMORYSTATUSEX MemStatus ;
   MemStatus.dwLength = sizeof(MemStatus);
   // Chamada à API WIndows para solicitar os dados
   GlobalMemoryStatusEx( &MemStatus ) ;
   DWORDLONG PhysicalMem = MemStatus.ullTotalPhys ;
   DWORDLONG SwapMem = MemStatus.ullTotalPageFile - PhysicalMem;

   // Exportação para a pilha de LUA
   lua_newtable( L ) ;
   lua_pushstring( L , "ram\0" ) ;
   lua_pushnumber( L , (lua_Number) PhysicalMem ) ;
   lua_rawset( L , - 3 ) ;
   lua_pushstring( L , "swap\0" ) ;
   lua_pushnumber( L , (lua_Number) SwapMem ) ;
   lua_rawset( L , - 3 ) ;
   lua_pushnil( L ) ;
   return 2 ;
}

/******************************************************************
 *
 *   Função _getNumberOfCpus
 *    Função utilizada para recuperar o número de processadores.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o número de processadores instalados na máquina. Também é
 *    inserido na pilha o valor nil, que é a descrição do erro.
 *    A API do Windows não retorna erros para esta função.
 *
 ******************************************************************/

static int _getNumberOfCpus( lua_State * L )
{
   lua_pushnumber( L , numberOfCpus ) ;
   lua_pushnil( L ) ;

   return 2 ;
}

/******************************************************************
 *
 *   Função _getByteOrderType
 *    Função utilizada para recuperar a ordenação de bytes.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    a ordenação de bytes na máquina. Em caso de erro, o valor
 *    retornado é nil e é inserida na pilha a descrição do erro.
 *    Em caso de sucesso o valor de erro nil é inserido na pilha.
 *
 ******************************************************************/

static int _getByteOrderType( lua_State * L )
{
    PPERF_DATA_BLOCK PerfData = QueryPerformanceData( ) ;
    if ( PerfData != NULL )
    {
       if( PerfData->LittleEndian == 0 )
       {
         free(PerfData);
         lua_pushnumber( L , BIG_ENDIAN ) ;
         lua_pushnil( L ) ;
         return 2 ;
       }
       else
       {
         free(PerfData);
         lua_pushnumber( L , LITTLE_ENDIAN ) ;
         lua_pushnil( L ) ;
         return 2 ;
       }
    }
    else
    {
        lua_pushnil( L ) ;
        lua_pushstring( L , "Erro ao acessar dados do WMI\0" ) ;
        return 2 ;
    }
}

/*****************************************************************
 *
 * Funções utilizadas para disparar e gerenciar processos .
 *
 *****************************************************************/

/******************************************************************
 *
 *   Função NeedToRefresh
 *    Função utilizada para saber se é necessário atualizar dados
 *    de processos, baseando-se em um time stamp e uma "idade"
 *    máxima para os dados. Esta função não é exportada para LUA,
 *    sendo de uso interno do módulo.
 *
 *   Parâmetros:
 *  - MaxOld:
 *    Tipo: double
 *    Descrição: Idade máxima para os dados de processo, em segundos.
 *
 *
 *   Valor de retorno:
 * - Tipo: BOOLEAN
 *   - Caso o tempo decorrido entre a última atualização dos dados e
 *   o momento em que essa função é chamada seja menor que MaxOld,
 *   retorna TRUE. Caso contrário retorna FALSE.
 *
 ******************************************************************/

static BOOLEAN NeedToRefresh( double MaxOld )
{
   FILETIME now ;
   GetSystemTimeAsFileTime( &now ) ;

   LARGE_INTEGER aux ;
   aux.LowPart = now.dwLowDateTime ;
   aux.HighPart = now.dwHighDateTime ;

   double NewTimeStamp = ( double ) aux.QuadPart ;
   double ProcessesDataAge = ( NewTimeStamp - Processes.TimeStamp ) / NS_TO_S ;

   if( ProcessesDataAge > MaxOld )
   {
      return TRUE ;
   }
   else
   {
      return FALSE ;
   }
}

/******************************************************************
 *
 *   Função RefreshIOInfo
 *    Função utilizada para recuperar informações de entrada/saída 
 *    da máquina. Esta função é de uso interno do módulo e não é 
 *    exportada para o ambiente LUA. Um intervalo mínimo de 10s 
 *    entre as chamadas é necessário para que os dados sejam atualizados.
 *
 *   Parâmetros:
 *   - PerfData
 *     Tipo: PPERF_DATA_BLOCK
 *     Descrição: Dados de desempenho obtidos pelo WMI.
 * 
 *   Valor de retorno:
 *   - A função não tem retorno.
 * 
 ******************************************************************/

static void RefreshIOInfo ( PPERF_DATA_BLOCK PerfData )
{
   PPERF_OBJECT_TYPE DiskObject = NULL ;
   PPERF_OBJECT_TYPE NetworkObject = NULL ;
   PPERF_INSTANCE_DEFINITION OneDisk = NULL ;
   PPERF_INSTANCE_DEFINITION OneNetwork = NULL ;
   unsigned long i;
 
   DiskObject = GetObjectFromIndex( PerfData, DISK_OBJECT_INDEX ) ;
   NetworkObject = GetObjectFromIndex( PerfData, NETWORK_OBJECT_INDEX ) ;
   if ( DiskObject == NULL || NetworkObject == NULL)
   {
      printf("Erro: GetObjectFromIndex\n");
      return;
   }
     
   // Variável utilizada para capturar os valores de escrita/leitura de todos os
   // discos (instância _Total)
   OneDisk = FirstInstance( DiskObject );

   IOData.DiskBytesRead = (double) GetCounterValueFromInstance( DiskObject, DISK_READBYTESSEC_COUNTER_INDEX , OneDisk ) ;
   IOData.DiskBytesWrite = (double) GetCounterValueFromInstance( DiskObject, DISK_WRITEBYTESSEC_COUNTER_INDEX , OneDisk ) ;

   unsigned long NumNetworks = NetworkObject->NumInstances ;
   // Variável utilizada para percorrer os vetores de interfaces de rede. Como
   // podem existir múltiplas interfaces e não há uma instância do tipo _Total
   // tal como acontece no disco, é preciso acumular os valores de entrada e 
   // saída de rede.
   OneNetwork = FirstInstance( NetworkObject );
   
   IOData.BytesIn = 0;
   IOData.BytesOut = 0; 

   for ( i = 0 ; i < NumNetworks ; i++ )
   {
   	   IOData.BytesIn += (double) GetCounterValueFromInstance( NetworkObject, NETWORK_BYTESRECVSEC_COUNTER_INDEX , OneNetwork ) ;
   	   IOData.BytesOut += (double) GetCounterValueFromInstance( NetworkObject, NETWORK_BYTESSENTSEC_COUNTER_INDEX , OneNetwork ) ;
	   
       OneNetwork = NextInstance( OneNetwork );
   }
      
   return;
}

/******************************************************************
 *
 *   Função RefreshAllProcessesInfo
 *    Função utilizada para recuperar número de processos em
 *      execução na máquina e atualizar os dados de processos. Esta
 *    função é de uso interno do módulo e não é exportada para o
 *    ambiente LUA. Um intervalo mínimo de 10s entre as chamadas é
 *    necessário para que os dados sejam atualizados.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: ambiente Lua usado para colocar as informações
 *               dos processos.
 *
 *   Valor de retorno:
 * - Tipo: unsigned long
 *   - Retorna o número de processos em execução. Em caso de erro
 *   retorna zero e libera o vetor que continha os dados de
 *   processos.
 *
 ******************************************************************/

static unsigned long RefreshAllProcessesInfo ( lua_State* L )
{
   PPERF_DATA_BLOCK PerfData = NULL ;
   PPERF_OBJECT_TYPE ProcessObject = NULL ;
   PPERF_INSTANCE_DEFINITION OneProcess = NULL ;
   static LARGE_INTEGER OldPoolingTime = { 0 } ;
   
   // Lê os processos do Cygwin
   lua_getglobal(L, "collectWinPID");
   if( lua_pcall(L, 0, 0, 0) ) {
      // retira a mensagem de erro da pilha
      const char* str = lua_tostring(L, -1);
      printf("Erro: collectWinPID %s\n", str);
      lua_pop(L, 1);
      return 0;
   }

   // Lê os dados do Windows   
   PerfData = QueryPerformanceData( ) ;

   if ( PerfData == NULL )
   {
      printf("Erro: QueryPerformanceData\n");
      return 0 ;
   }

   if(enableHistoric == TRUE) {
   	  RefreshIOInfo(PerfData);
   }

   ProcessObject = GetObjectFromIndex( PerfData, PROCESS_OBJECT_INDEX ) ;
   if ( ProcessObject == NULL )
   {
      free( PerfData ) ;
      printf("Erro: GetObjectFromIndex\n");
      return 0 ;
   }

   // Novo timer do sistema.
   LARGE_INTEGER NewPoolingTime = PerfData->PerfTime100nSec ;

   // Tempo decorrido entre as chamadas. Mínimo de 10s para atualização de dados.
   double deltaPooling = (double) ( NewPoolingTime.QuadPart - OldPoolingTime.QuadPart ) ;

   // Número de processos em execução durante a última chamada.
   unsigned long OldNumProcesses = Processes.NumProcesses ;

   if  ( Processes.aProcessData != NULL )
   {
      free ( Processes.aProcessData ) ;
      Processes.index.clear();
      Processes.children.clear();
   }

   //A instância _Total de process não entra
   unsigned long NumProcesses = ProcessObject->NumInstances - 1 ;

   Processes.aProcessData = ( PPROCESS ) malloc ( NumProcesses * sizeof( PROCESS ) ) ;
   if (Processes.aProcessData == NULL) {
      printf("Erro: malloc\n");
      return 0;
   }

   // Variáveis utilizadas para percorrer os vetores de processos.
   MapCPU::const_iterator cpuIter ;
   unsigned long i;
   OneProcess = FirstInstance( ProcessObject );

   for ( i = 0 ; i < NumProcesses ; i++ )
   {
       LONGLONG Freq = ProcessObject->PerfFreq.QuadPart ;
       LONGLONG Time = ProcessObject->PerfTime.QuadPart ;
       LONGLONG Counter = GetLLCounterValueFromInstance( ProcessObject, ELAPSED_TIME_COUNTER_INDEX , OneProcess ) ;
       double tmpCPUPerc = (double) GetCounterValueFromInstance( ProcessObject, PROCESSOR_TIME_COUNTER_INDEX, OneProcess ) ;

       Processes.aProcessData[ i ].PID = ( DWORD ) GetCounterValueFromInstance( ProcessObject, PROCESS_PID_COUNTER_INDEX, OneProcess);
       Processes.aProcessData[ i ].PPID = ( DWORD ) GetCounterValueFromInstance( ProcessObject, PROCESS_PPID_COUNTER_INDEX, OneProcess);
       Processes.aProcessData[ i ].MemoryUsed = ( double ) ( GetCounterValueFromInstance( ProcessObject, PROCESS_WORKINGSET_COUNTER_INDEX, OneProcess) );
       Processes.aProcessData[ i ].SwapMemoryUsed = ( double )  GetCounterValueFromInstance( ProcessObject, PROCESS_SWAPMEMORY_COUNTER_INDEX , OneProcess ) ;
       Processes.aProcessData[ i ].UserTime = ( double ) GetLLCounterValueFromInstance( ProcessObject, PROCESS_USERTIME_COUNTER_INDEX, OneProcess ) / NS_TO_S ;
       Processes.aProcessData[ i ].KernelTime = ( double ) GetLLCounterValueFromInstance( ProcessObject, PROCESS_KERNELTIME_COUNTER_INDEX, OneProcess ) / NS_TO_S ;
       Processes.aProcessData[ i ].WallTime = ( double )( ( Time - Counter ) / Freq ) ;
       Processes.aProcessData[ i ].VirtualBytes = (double) GetCounterValueFromInstance( ProcessObject, PROCESS_VIRTUALBYTES_COUNTER_INDEX , OneProcess ) ;
       Processes.aProcessData[ i ].DiskBytesRead = IOData.DiskBytesRead;
       Processes.aProcessData[ i ].DiskBytesWrite = IOData.DiskBytesWrite;
       Processes.aProcessData[ i ].BytesIn = IOData.BytesIn;
       Processes.aProcessData[ i ].BytesOut = IOData.BytesOut;
       
       OneProcess = NextInstance( OneProcess );

       // Verifica se o processo existia na coleta anterior
       cpuIter = Processes.oldCPU->find( Processes.aProcessData[ i ].PID );
       if( cpuIter != Processes.oldCPU->end() )
       {
           // Percentual normalizado pelo número de CPUs
           Processes.aProcessData[ i ].CPU_PERC = ( ((tmpCPUPerc - cpuIter->second) / deltaPooling ) / (double)numberOfCpus) * 100;
       }
       else {
           Processes.aProcessData [ i ].CPU_PERC = 0 ;
       }

       // Cria um indice para os processos
       Processes.index.insert( Process_Pair( Processes.aProcessData[ i ].PID, i ));

       // XXX Um processo no Windows pode ser pai dele mesmo?!?
       if( Processes.aProcessData[ i ].PPID != Processes.aProcessData[ i ].PID )
           Processes.children.insert( Process_Pair( Processes.aProcessData[ i ].PPID, i ));

       // Guarda o valor da CPU para ser usada na próxima coleta
       Processes.currCPU->insert( CPU_Pair( Processes.aProcessData[ i ].PID, tmpCPUPerc ));
   }

   // Descarta valores antigos da CPU
   Processes.oldCPU->clear();

   // Guarda os valores atuais para a próxima chamada
   MapCPU *tmpCPU = Processes.oldCPU;
   Processes.oldCPU = Processes.currCPU;
   Processes.currCPU = tmpCPU;

   Processes.NumProcesses = NumProcesses ;
   OldPoolingTime = NewPoolingTime ;
   Processes.TimeStamp = ( double ) NewPoolingTime.QuadPart ;
   
   free( PerfData ) ;
   return NumProcesses ;
}

/******************************************************************
 *
 *   Função _getProcessesByPID
 *    Função utilizada para recuperar o PID dos filhos de um dado
 *    processo.
 *
 * -  L:
 *    Tipo: lua_State
 *    Descrição: ambiente Lua usado para colocar as informações
 *               dos processos.
 * -  pid:
 *    Tipo: DWORD
 *    Descrição: PID do pai dos processos a serem recuperados.
 *
 * -  children:
 *    Tipo: DWORD**
 *    Descrição: Retorna o PID dos processos filho.
 *
 * -  size:
 *    Tipo: DWORD*
 *    Descrição: Retorna o número de processos filho.
 *
 *   Valor de retorno:
 *   -  Retorna TRUE em caso normal e FALSE caso haja erro.
 *      No caso de erro, a pilha Lua conterá 'nil' e a mensagem de erro.
 *
 ******************************************************************/

static BOOL _getProcessesByPID( lua_State* L, DWORD pid , DWORD** procs , unsigned long* size )
{
   unsigned long i = 0;
   DWORD proc = 0;

   // Busca a lista de processos filhos no Cygwin
   lua_getglobal( L, "getWinPID" );
   lua_pushnumber( L, pid );
   if( lua_pcall(L, 1, 1, 0) != 0 ) {
      lua_pushnil( L );
      lua_pushstring( L, "Erro ao ler informações sobre os processos.");
      return FALSE ;
   }

   // PID não encontrado
   if( lua_isnil(L, -1) ) {
      lua_pushstring(L, "Processo não encontrado.");
      return FALSE;
   }

   // Número de processos encontrados (o PID do pai pode estar incluso)
   *size = (DWORD) luaL_getn( L, -1 );
   if( (*size) == 0 ) {
      return TRUE;
   }

   // Aloca espaço para os PIDs
   *procs = ( DWORD * ) malloc( (*size) * sizeof( DWORD ) ) ;
   if ((*procs) == NULL) {
      lua_pushnil( L );
      lua_pushstring( L, "Erro: memória insuficiente.");

      return FALSE;
   }

   // Coloca os PIDs no vetor
   for( i = 0 ; i < (*size) ; i++ ) {
      lua_pushnumber( L, i+1 );
      lua_gettable( L, -2 );

      proc = (DWORD) lua_tonumber( L, -1 );
      (*procs)[ i ] = proc ;
      lua_pop(L, 1);
   }

   return TRUE;
}

/******************************************************************
 *
 *   Função _collectChildrenInfo
 *    Função utilizada para recuperar as informações sobre um 
 *    processo. Esta função é de uso interno do módulo e NÃO é 
 *    exportada para o ambiente LUA.
 *
 * -  L:
 *    Tipo: lua_State
 *    Descrição: ambiente Lua usado para colocar as informações
 *               dos processos. No topo da pilha fica a tabela
 *               que conterá as informações sobre o processo. 
 * -  pid:
 *    Tipo: DWORD
 *    Descrição: PID do pai dos processos a serem recuperados.
 *
 *   Valor de retorno:
 *   -  Retorna FALSE caso o processo não foi localizado. Caso 
 *      contrário, TRUE.
 *
 ******************************************************************/

static bool _collectProcessInfo(lua_State *L, DWORD pid)
{
   unsigned long idx;
   MapProcess::const_iterator processIter;

   // Busca o PID
   processIter = Processes.index.find( (DWORD) pid );

   // Como a coleta de Lua é em um momento diferente da coleta do
   // Windows, um processo pode não aparecer.
   if( processIter == Processes.index.end() )
      return FALSE;

   // Posição do processo no vetor
   idx = processIter->second;

   // Prepara o índice para os dados do processo
   lua_newtable( L ) ;

   // Preenche a tabela do processo
   lua_pushstring( L, "pid" ) ;
   lua_pushnumber( L, Processes.aProcessData[ idx ].PID );
   lua_rawset( L, -3 );

   lua_pushstring( L, "ppid" );
   lua_pushnumber( L, Processes.aProcessData[ idx ].PPID );
   lua_rawset( L, -3 );

   lua_pushstring(L, "memoryRamSizeMb");
   lua_pushnumber(L, Processes.aProcessData[ idx ].MemoryUsed / (1024 * 1024));
   lua_rawset(L, -3);

   lua_pushstring(L, "memorySwapSizeMb");
   lua_pushnumber(L, Processes.aProcessData[ idx ].SwapMemoryUsed / (1024 * 1024));
   lua_rawset(L, -3);
   
   lua_pushstring(L, "CPUTimeSec");
   lua_pushnumber(L, Processes.aProcessData[ idx ].UserTime +
                     Processes.aProcessData[ idx ].KernelTime );
   lua_rawset(L, -3);

   lua_pushstring(L, "wallTimeSec");
   lua_pushnumber(L, Processes.aProcessData[ idx ].WallTime );
   lua_rawset(L, -3);

   lua_pushstring(L, "CPUPerc");
   lua_pushnumber(L, Processes.aProcessData[ idx ].CPU_PERC );
   lua_rawset(L, -3);
   
   lua_pushstring(L, "userTimeSec");
   lua_pushnumber(L, Processes.aProcessData[ idx ].UserTime );
   lua_rawset(L, -3);
   
   lua_pushstring(L, "systemTimeSec");
   lua_pushnumber(L, Processes.aProcessData[ idx ].KernelTime );
   lua_rawset(L, -3);
   
   lua_pushstring(L, "virtualMemorySizeMB");
   lua_pushnumber(L, Processes.aProcessData[ idx ].VirtualBytes );
   lua_rawset(L, -3);
   
   lua_pushstring(L, "diskBytesReadKB");
   lua_pushnumber(L, Processes.aProcessData[ idx ].DiskBytesRead / 1024);
   lua_rawset(L, -3);
   
   lua_pushstring(L, "diskBytesWriteKB");
   lua_pushnumber(L, Processes.aProcessData[ idx ].DiskBytesWrite / 1024);
   lua_rawset(L, -3);
   
   lua_pushstring(L, "bytesInKB");
   lua_pushnumber(L, Processes.aProcessData[ idx ].BytesIn / 1024);
   lua_rawset(L, -3);
   
   lua_pushstring(L, "bytesOutKB");
   lua_pushnumber(L, Processes.aProcessData[ idx ].BytesOut / 1024);
   lua_rawset(L, -3);

   lua_pushstring(L, "state");
   lua_pushstring(L, "RUNNING" );
   lua_rawset(L, -3);

   lua_pushstring(L, "processorId");
   lua_pushnumber(L, 0);
   lua_rawset(L, -3);

   lua_pushstring(L, "command");
   lua_pushstring(L, "unknown" );
   lua_rawset(L, -3);

   lua_pushstring(L, "execHost");
   lua_pushstring(L, "unknown" );
   lua_rawset(L, -3);

   return TRUE;
}

/******************************************************************
 *
 *   Função _getCommandAllInfo
 *    Função utilizada para recuperar uma lista de todos os filhos
 *    de um processo e suas respectivas infomações.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 *
 * - MaxOld:
 *   Tipo: number
 *   Descrição: "Idade" máxima que a informação pode ter.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha uma tabela contendo os processos
 *      filhos e seus dados de CPU, memória, etc.
 *
 ******************************************************************/

static int _getCommandAllInfo(lua_State *L)
{
   MapProcess::const_iterator processIter;
   DWORD *procs = NULL;
   unsigned long nProcs = 0;
   unsigned long count = 0;
   unsigned long idx = 0;
   unsigned long i = 0;

   // Recebe "idade" máxima para dados de processos
   double MaxOld = lua_tonumber( L , -1 ) ;

   // Teste para saber se é necessário atualizar dados de processos
   if( NeedToRefresh( MaxOld ) == TRUE )
   {
      if ( RefreshAllProcessesInfo( L ) == 0 )
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Falha ao atualizar dados de processos.\0" ) ;
         return 2 ;
      }
   }

   // Procura pelo uso de cpu do processo e retorna
   if ( lua_isuserdata( L, -2 ) )
   {
      // Pega o Handle do comando na pilha
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , -2 ) ;

      // Recupera os processos do Cygwin
      if( !_getProcessesByPID(L, CommandHandle->pid, &procs, &nProcs) ) {
         // Em caso de erro, este já está na pilha Lua
         return 2;
      }

      // Tabela de resposta que conterá a lista de informações sobre 
      // os processos.
      lua_newtable( L );
      idx = lua_gettop( L );

      if( nProcs == 0 ) {
         return 1;
      }

      // Coleta as informações do filho
      count = 1;
      for( i = 0; i < nProcs; i++ ) {
         lua_pushnumber(L, count);
         // Salva na tabela 'idx' as informações sobre o processo
         if( _collectProcessInfo(L, procs[i]) ) {
            lua_rawset(L, idx);
            count++;
         }
         else {
            lua_pop(L, 1);
         }
      }

      free( procs );

      return 1;
   }

   // Caso o Handle seja inválido, retorna o erro
   lua_pushnil( L ) ;
   lua_pushstring( L , "Handle de comando inválido.\0" ) ;
   return 2 ;
}


/******************************************************************
 *
 *   Função _getProcessCpuLoad
 *    Função utilizada para recuperar o uso de CPU de um processo
 *    cujo o handle é passado na pilha como parâmetro. Esta função
 *    é registrada como getcommandcpuload na tabela servermanager,
 *    global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 * - MaxOld:
 *   Tipo: number
 *   Descrição: "Idade" máxima que a informação pode ter.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o uso de cpu do comando. Em caso de erro, o valor retornado
 *    é nil e é inserida na pilha a descrição do erro. Em caso de
 *    sucesso o valor de erro nil é inserido na pilha.
 *
 ******************************************************************/

static int _getProcessCpuLoad( lua_State * L )
{
   // Recebe "idade" máxima para dados de processos
   double MaxOld = lua_tonumber( L , -1 ) ;

   // Teste para saber se é necessário atualizar dados de processos
   if( NeedToRefresh( MaxOld ) == TRUE )
   {
      if ( RefreshAllProcessesInfo( L ) == 0 )
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Falha ao atualizar dados de processos.\0" ) ;
         return 2 ;
      }
   }

   // Procura pelo uso de cpu do processo e retorna
   if ( lua_isuserdata( L, -2 ) )
   {
      // Pega o Handle do comando na pilha
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , -2 ) ;

      DWORD PID = CommandHandle->pid ;
      // Procura no índice de processos
      MapProcess::const_iterator iter = Processes.index.find(PID) ;
      if( iter != Processes.index.end() ) {
         lua_pushnumber( L , Processes.aProcessData[ iter->second ].CPU_PERC ) ;
         lua_pushnil( L ) ;
         return 2 ;
      }

      // Caso não tenha achado o processo em questão, retorna o erro
      lua_pushnil( L ) ;
      lua_pushstring( L , "Comando não está executando.\0" ) ;
      return 2 ;
   }
   // Caso o Handle seja inválido, retorna o erro
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Handle de comando inválido.\0" ) ;
      return 2 ;
   }
}

/******************************************************************
 *
 *   Função _getProcessMemoryLoad
 *    Função utilizada para recuperar o uso de memória de um
 *    processo cujo o handle é passado na pilha como parâmetro.
 *    Esta  função é registrada como getcommandmemoryload na tabela
 *    servermanager, global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 * - MaxOld:
 *   Tipo: number
 *   Descrição: Tempo máximo que a informação pode ter. Não é
 *              utilizado.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o uso de memória do comando. Este uso é inserido em uma
 *    tabela contendo os campos "ram" e "swap". Em caso de erro,
 *    o valor retornado é nil e é inserida na pilha a descrição do
 *    erro. Em caso de sucesso o valor de erro nil é inserido na pilha.
 *
 ******************************************************************/

static int _getProcessMemoryLoad( lua_State *L )
{
   // Recebe "idade" máxima para dados de processos
   double MaxOld = lua_tonumber( L , -1 ) ;

   // Teste para saber se é necessário atualizar dados de processos
   if( NeedToRefresh( MaxOld ) == TRUE )
   {
      if ( RefreshAllProcessesInfo( L ) == 0 )
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Falha ao atualizar dados de processos.\0" ) ;
         return 2 ;
      }
   }

   // Procura pelo uso de memória do processo e retorna
   if ( lua_isuserdata( L, -2 ) )
   {
      // Pega o Handle do comando na pilha
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , -2 ) ;

      DWORD PID = CommandHandle->pid ;
      // Procura no índice de processos
      MapProcess::const_iterator iter = Processes.index.find(PID) ;
      if( iter != Processes.index.end() )
      {
         lua_newtable( L ) ;

         lua_pushstring( L , "ram\0" ) ;
         lua_pushnumber( L , Processes.aProcessData[ iter->second ].MemoryUsed ) ;
         lua_rawset( L , - 3 ) ;

         lua_pushstring( L , "swap\0" ) ;
         lua_pushnumber( L , Processes.aProcessData[ iter->second ].SwapMemoryUsed ) ;
         lua_rawset( L , - 3 ) ;

         lua_pushnil( L ) ;

         return 2 ;
      }

      // Caso não tenha achado o processo em questão, retorna o erro
      lua_pushnil( L ) ;
      lua_pushstring( L , "Comando não está executando.\0" ) ;
      return 2 ;
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Handle de comando inválido\0" ) ;
      return 2 ;
   }
}

/******************************************************************
 *
 *   Função _getJobsInfo
 *    Esta função é registrada como getjobsinfo
 *    na tabela servermanager, global no ambiente Lua.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para Lua na tabela
 *               servermanager, é necessário saber qual o State.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o valor nil sinalizando que, por enquanto, não foi definido
 *    qual o comportamento que essa função deve ter no Windows.
 ******************************************************************/

static int _getJobsInfo( lua_State * L )
{
	   lua_pushnil( L ) ;

	   return 1 ;
}


/******************************************************************
 *
 *   Função _getNumberOfJobs
 *    Função utilizada para obter o número de jobs em 
 *    execução no servidor.
 *    Esta  função é registrada como getnumberofjobs
 *    na tabela servermanager, global no ambiente Lua.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para Lua na tabela
 *               servermanager, é necessário saber qual o State.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o número de jobs em execução no servidor.
 ******************************************************************/

static int _getNumberOfJobs( lua_State * L )
{
	   int count = 0;
	   // Recebe "idade" máxima para dados de processos
	   double MaxOld = lua_tonumber( L , -1 ) ;

	   // Teste para saber se é necessário atualizar dados de processos
	   if( NeedToRefresh( MaxOld ) == TRUE )
	   {
	      if ( RefreshAllProcessesInfo( L ) == 0 )
	      {
	         lua_pushnil( L ) ;
	         lua_pushstring( L , "Falha ao atualizar dados de processos.\0" ) ;
	         return 2 ;
	      }
	   }

	   //coloca a tabela global que armazena
	   //os Comandos de lua no topo da pilha
	   lua_getglobal( L, "COMMAND" ) ;
	   
	   //coloca no topo da pilha o array que
	   //que possui o registro de todos os comandos
	   lua_getfield( L, -1, "array" ) ;

	   lua_pushnil( L ) ;
	   //itera por todos os comandos
	   while (lua_next( L, -2 ) != 0){

		   count = count + 1;

		   //remove o valor do comando e mantem a chave
		   //para a iteração continuar
		   lua_pop( L, 1 );
	   }

	   lua_pushnumber( L , count);
	   lua_pushnil( L );

	   return 2 ;
}


/******************************************************************
 *
 *   Função _getProcessTimes
 *    Função utilizada para recuperar os tempos de um processo cujo
 *    o handle é passado na pilha como parâmetro. Esta  função é
 *    registrada como getcommandtimeusage na tabela servermanager,
 *    global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 * - MaxOld:
 *   Tipo: number
 *   Descrição: Tempo máximo que a informação pode ter. Não é
 *              utilizado.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    os tempos do comando. Estes tempos são inserido em uma
 *    tabela contendo os campos "user", "system" e "elapsed". Em
 *    caso de erro, o valor retornado é nil e é inserida na pilha
 *    a descrição do erro. Em caso de sucesso o valor de erro nil
 *    é inserido na pilha.
 *
 ******************************************************************/

static int _getProcessTimes( lua_State * L )
{
   // Recebe "idade" máxima para dados de processos
   double MaxOld = lua_tonumber( L , -1 ) ;

   // Teste para saber se é necessário atualizar dados de processos
   if( NeedToRefresh( MaxOld ) == TRUE )
   {
      if ( RefreshAllProcessesInfo( L ) == 0 )
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Falha ao atualizar dados de processos.\0" ) ;
         return 2 ;
      }
   }

   // Procura pelo uso de memória do processo e retorna
   if ( lua_isuserdata( L, -2 ) )
   {
      // Pega o Handle do comando na pilha
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , -2 ) ;

      DWORD PID = CommandHandle->pid ;
      // Procura no índice de processos
      MapProcess::const_iterator iter = Processes.index.find(PID) ;
      if( iter != Processes.index.end() )
      {
         lua_newtable( L ) ;

         lua_pushstring( L , "user\0" ) ;
         lua_pushnumber( L , Processes.aProcessData[ iter->second ].UserTime ) ;
         lua_rawset( L , - 3 ) ;

         lua_pushstring( L , "system\0" ) ;
         lua_pushnumber( L , Processes.aProcessData[ iter->second ].KernelTime ) ;
         lua_rawset( L , - 3 ) ;

         lua_pushstring( L , "elapsed\0" ) ;
         lua_pushnumber( L , Processes.aProcessData[ iter->second ].WallTime ) ;
         lua_rawset( L , - 3 ) ;

         lua_pushnil( L ) ;
         return 2 ;
      }
      // Caso não esteja mais rodando, retorna os tempos finais do processo

      // Estrutras passadas para a função da API do Windows
      // para pegar tempos dos processos.
      FILETIME ftUser ;
      FILETIME ftKernel ;
      FILETIME ftCreation ;
      FILETIME ftExit ;

      // Variável utilizada para derivar os dados
      // recebidos da API para segundos
      LARGE_INTEGER auxTime , auxTime1 ;

      double UserTime , KernelTime , WallTime ;

      // Chamada à API do Windows para obter os tempos do processo terminado.
      // É necessário converter os tempos para segundos.
      if ( GetProcessTimes( CommandHandle->hProcess , &ftCreation , &ftExit , &ftKernel , &ftUser ) == TRUE )
      {
         auxTime.HighPart = ftUser.dwHighDateTime ;
         auxTime.LowPart = ftUser.dwLowDateTime ;
         UserTime = ( double ) auxTime.QuadPart / NS_TO_S ;


         auxTime.HighPart = ftKernel.dwHighDateTime ;
         auxTime.LowPart = ftKernel.dwLowDateTime ;
         KernelTime = ( double ) auxTime.QuadPart / NS_TO_S ;

         auxTime.HighPart = ftCreation.dwHighDateTime ;
         auxTime.LowPart = ftCreation.dwLowDateTime ;
         auxTime1.HighPart = ftExit.dwHighDateTime ;
         auxTime1.LowPart = ftExit.dwLowDateTime ;

         WallTime = ( double )  ( auxTime1.QuadPart - auxTime.QuadPart ) / NS_TO_S ;

         // Retorna os tempos
         lua_newtable( L ) ;
         lua_pushstring( L , "user\0" ) ;
         lua_pushnumber( L , UserTime ) ;
         lua_rawset( L , - 3 ) ;
         lua_pushstring( L , "system\0" ) ;
         lua_pushnumber( L , KernelTime ) ;
         lua_rawset( L , - 3 ) ;
         lua_pushstring( L , "elapsed\0" ) ;
         lua_pushnumber( L , WallTime ) ;
         lua_rawset( L , - 3 ) ;
         lua_pushnil( L ) ;
         return 2 ;
      }
      // Caso haja falha na aquisição dos tempos finais, retorna o erro
      else
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Erro ao recuperar tempos finais do comando.\0" ) ;
         return 2 ;
      }
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Handle de comando inválido.\0" ) ;
      return 2 ;
   }
}

/******************************************************************
 *
 *   Função _getProcessStatus
 *    Função utilizada para recuperar o status de um processo cujo
 *    o handle é passado na pilha como parâmetro. Esta  função é
 *    registrada como getcommandstatus na tabela servermanager,
 *    global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 * - MaxOld:
 *   Tipo: number
 *   Descrição: Tempo máximo que a informação pode ter. Não é
 *              utilizado.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o status do comando. Este status pode possuir o valor RUNNING
 *    ou FINISHED. Em caso de erro, o valor retornado é nil e é
 *    inserida na pilha a descrição do erro. Em caso de sucesso o
 *    valor de erro nil é inserido na pilha.
 *
 ******************************************************************/

static int _getProcessStatus( lua_State *L )
{
   // Recebe "idade" máxima para dados de processos
   double MaxOld = lua_tonumber( L , -1 ) ;

   // Teste para saber se é necessário atualizar dados de processos
   if( NeedToRefresh( MaxOld ) == TRUE )
   {
      if ( RefreshAllProcessesInfo( L ) == 0 )
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Falha ao atualizar dados de processos.\0" ) ;
         return 2 ;
      }
   }

   // Procura pelo uso de memória do processo e retorna
   if ( lua_isuserdata( L, -2 ) )
   {
      // Pega o Handle do comando na pilha
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , -2 ) ;

      DWORD PID = CommandHandle->pid ;
      // Procura no índice de processos
      MapProcess::const_iterator iter = Processes.index.find(PID) ;
      if( iter != Processes.index.end() )
      {
         lua_pushnumber( L , RUNNING ) ;
         lua_pushnil( L ) ;
         return 2 ;
      }

      // Processo não está rodando
      lua_getglobal(L, "_checkforerror");
      lua_pushlightuserdata( L , ( void * ) CommandHandle ) ;
      lua_pcall(L, 1, 0, 0);

      lua_pushnumber( L , FINISHED ) ;
      lua_pushnil( L ) ;
      return 2 ;
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Handle de comando inválido\0" ) ;
      return 2 ;
   }
}

/******************************************************************
 *
 *   Função _getProcessExecHost
 *    Função utilizada para recuperar o host de um processo cujo
 *    o handle é passado na pilha como parâmetro. Esta  função é
 *    registrada como getcommandexechost na tabela servermanager,
 *    global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o host que está executando o comando. 
 *    Em caso de erro, o valor retornado é nil e é
 *    inserida na pilha a descrição do erro. Em caso de sucesso o
 *    valor de erro nil é inserido na pilha.
 *
 ******************************************************************/

static int _getProcessExecHost( lua_State *L )
{
   if ( lua_isuserdata( L, -2 ) )
   {
      // Pega o Handle do comando na pilha
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , -2 ) ;

      HANDLE hName = CommandHandle->hName ;

      if (hName) {
        lua_pushstring( L , (const char*) hName ) ;
      }
      else {
        lua_pushstring( L , "unknown" ) ; // XXX - Nao podemos usar gethostname?
      }
      lua_pushnil( L ) ;
      return 2 ;
   }

   lua_pushnil( L ) ;
   lua_pushstring( L , "Handle de comando inválido\0" ) ;
   return 2 ;
}


/******************************************************************
 *
 *      Função _getProcessParentPid
 *      Função utilizada para recuperar o PID do pai de um processo
 *    cujo o handle é passado na pilha como parâmetro. Esta  função é
 *    registrada como getcommandstatus na tabela servermanager,
 *    global no ambiente LUA.
 *
 *      Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 * - MaxOld:
 *   Tipo: number
 *   Descrição: Tempo máximo que a informação pode ter.
 *
 *      Valor de retorno:
 *      -  A função retorna, na pilha do state passado como parâmetro,
 *    o PID do pai do processo. Em caso de erro, o valor retornado
 *    é nil e é inserida na pilha a descrição do erro. Em caso de
 *    sucesso o valor de erro nil é inserido na pilha.
 *
 ******************************************************************/

static int _getProcessParentPid( lua_State *L )
{
   // Recebe "idade" máxima para dados de processos
   double MaxOld = lua_tonumber( L , 2 ) ;

   // Teste para saber se é necessário atualizar dados de processos
   if( NeedToRefresh( MaxOld ) == TRUE )
   {
      if ( RefreshAllProcessesInfo( L ) == 0 )
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Falha ao atualizar dados de processos.\0" ) ;
         return 2 ;
      }
   }

   // Procura pelo PPID do processo e retorna
   if ( lua_isuserdata( L, 1 ) )
   {
      // Pega o Handle do comando na pilha
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , 1 ) ;

      DWORD PID = CommandHandle->pid ;

      // Procura no índice de processos
      MapProcess::const_iterator iter = Processes.index.find(PID) ;
      if( iter != Processes.index.end() )
      {
         lua_pushnumber( L , Processes.aProcessData[ iter->second ].PPID ) ;
         lua_pushnil( L ) ;
         return 2 ;
      }

      // Processo não está rodando
      lua_pushnil( L ) ;
      lua_pushstring( L , "Processo terminado.\0" ) ;
      return 2 ;
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Handle de comando invalido\0" ) ;
      return 2 ;
   }
}

/******************************************************************
 *
 *   Função _retrievecommandhandle
 *      Função utilizada para criar um handle a partir de dados
 *    persistentes do processo. Função registrada como
 *    retrievecommandhandle na tabela servermanager, global no
 *    ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - pdata:
 *   Tipo: tabela
 *   Descrição: Esta função pega na pilha de LUA a tabela criada
 *              pela função _getcommandpersistentdata
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    um handle para o comando. Em caso de erro, o valor retornado
 *    é nil e é inserida na pilha a descrição do erro. Em caso de
 *    sucesso o valor de erro nil é inserido na pilha.
 *
 ******************************************************************/

static int _retrievecommandhandle( lua_State * L )
{
  DWORD pid ;
  HANDLE hProcess ;
  PCOMMAND ProcessData ;
  FILETIME ftCreation ;
  FILETIME dummy ;
  FILETIME auxTime ;
  if( lua_istable( L , 1 ) )
  {
    lua_rawgeti ( L, 1 , 1 ) ;
    pid = (DWORD) lua_tonumber( L , - 1 ) ;
    lua_rawgeti ( L, 1 , 2 ) ;
    auxTime.dwLowDateTime =  (unsigned long)lua_tonumber( L , -1 ) ;
    lua_rawgeti ( L, 1 , 3 ) ;
    auxTime.dwHighDateTime =  (unsigned long)lua_tonumber( L , -1 ) ;

    hProcess = OpenProcess( PROCESS_ALL_ACCESS , FALSE , pid ) ;
    if( !hProcess )
    {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Falha ao recuperar handle para o processo.\0" ) ;
      return 2 ;
    }

    if ( GetProcessTimes( hProcess , &ftCreation , &dummy , &dummy , &dummy ) == TRUE )
    {
      if( (auxTime.dwHighDateTime == ftCreation.dwHighDateTime) &&
          (auxTime.dwLowDateTime == ftCreation.dwLowDateTime))
      {
        //Sem erros, retorna o handle
        // Desaloca em releasecommandinfo
        ProcessData = ( PCOMMAND ) malloc( sizeof( COMMAND ) ) ; 
        if (ProcessData == NULL) {
           lua_pushnil( L ) ;
           lua_pushstring( L , "Falha ao alocar memória para novo handle.\0" ) ;
           return 2 ;
        }
        ProcessData->pid = pid ;
        ProcessData->CommandId = NULL;
        ProcessData->hStdIn = NULL;
        ProcessData->hStdOut = NULL;
        ProcessData->hStdError = NULL;
        ProcessData->hName = NULL ;
        ProcessData->hProcess = hProcess ;
        lua_pushlightuserdata( L , ( void * ) ProcessData ) ;
        lua_pushnil( L ) ;
        return 2 ;
      }
      else
      {
        lua_pushnil( L ) ;
        lua_pushstring( L , "Processos diferem na criacao.\0" ) ;
        return 2 ;
      }
    }
    else
    {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Erro ao recuperar data de criacao do processo.\0" ) ;
      return 2 ;
    }
  }
  else
  {
    lua_pushnil( L ) ;
    lua_pushstring( L , "Tabela invalida para recuperar handle.\0" ) ;
    return 2 ;
  }
}

/******************************************************************
 *
 *   Função _getcommandpersistentdata
 *      Função utilizada para guardar dados de um processo cujo
 *    o handle é passado na pilha como parâmetro. Com estes dados,
 *    deve ser possível se refazer o handle do comando. Esta  função
 *    é registrada como getcommandpersistentdata na tabela
 *    servermanager, global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    uma tabela com os campos pid e data de criação. Em caso de erro,
 *    o valor retornado é nil e é inserida na pilha a descrição do
 *    erro. Em caso de sucesso o valor de erro nil é inserido na
 *    pilha.
 *
 ******************************************************************/

static int _getcommandpersistentdata( lua_State *L )
{
   DWORD pid ;
   HANDLE pHandle ;
   FILETIME ftCreation ;
   FILETIME dummy ;

   // Procura pelo uso de memória do processo e retorna
   if ( lua_isuserdata( L, 1 ) )
   {
      // Pega o Handle do comando na pilha
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , 1 ) ;

      pid = CommandHandle->pid ;
      pHandle = CommandHandle->hProcess ;

      if ( GetProcessTimes( pHandle , &ftCreation , &dummy , &dummy , &dummy ) == TRUE )
      {
        //Sem erros, retorna a tabela
        lua_newtable( L ) ;
        lua_pushnumber( L , 1 ) ;
        lua_pushnumber( L , pid ) ;
        lua_rawset( L , - 3 ) ;
        lua_pushnumber( L , 2 ) ;
        lua_pushnumber( L , ftCreation.dwLowDateTime ) ;
        lua_rawset( L , - 3 ) ;
        lua_pushnumber( L , 3 ) ;
        lua_pushnumber( L , ftCreation.dwHighDateTime ) ;
        lua_rawset( L , - 3 ) ;
        lua_pushnil( L ) ;
        return 2 ;
      }
      else
      {
        lua_pushnil( L ) ;
        lua_pushstring( L , "Falha ao obter data de criacao do processo.\0" ) ;
        return 2 ;
      }
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Handle de comando inválido.\0" ) ;
      return 2 ;
   }
}

/******************************************************************
 *
 *   Função setStartupHandles
 *      Função utilizada para abrir os arquivos que redefinem as 
 *    saídas e entrada padrão de um processo e preencher os respectivos
 *    campos na estrutura STARTUPINFO.
 *
 *   Parâmetros:
 * -  si:
 *    Tipo: STARTUPINFO*
 *    Descrição: Estrutura que informa os dados para inicialização
 *               do novo processo.
 * - inFileName:
 *   Tipo: string
 *   Descrição: Nome do arquivo que redefine a entrada padrão do
 *              processo. Pode conter NULL ou "\0".
 *
 * - outFileName:
 *   Tipo: string
 *   Descrição: Nome do arquivo que redefine a saída padrão do
 *              processo. Pode conter NULL ou "\0".
 *
 * - errorFileName:
 *   Tipo: string
 *   Descrição: Nome do arquivo que redefine a saída de erro do
 *              processo. Pode conter NULL ou "\0".
 *
 *   Valor de retorno:
 *   -  Em caso de erro, uma string com mensagem indicativa do erro.
 *      Em caso de sucesso, NULL.
 *
 ******************************************************************/

static char* setStartupHandles (STARTUPINFO *si, const char *inFileName,
  const char *outFileName, const char *errorFileName ) {

   HANDLE hInFile = GetStdHandle(STD_INPUT_HANDLE) ;
   HANDLE hOutFile = GetStdHandle(STD_OUTPUT_HANDLE) ;
   HANDLE hErrorFile = GetStdHandle(STD_ERROR_HANDLE) ;

   SECURITY_ATTRIBUTES fileHandlesSecurity ;
   fileHandlesSecurity.bInheritHandle = TRUE ;
   fileHandlesSecurity.lpSecurityDescriptor = NULL ;
   fileHandlesSecurity.nLength = sizeof( SECURITY_ATTRIBUTES ) ;

   if (( inFileName != NULL ) && ( strlen( inFileName ) > 0 )) {
      hInFile = CreateFile( inFileName , // Nome do arquivo
                            FILE_WRITE_DATA | FILE_READ_DATA , // Permissões de 
                            FILE_SHARE_READ | FILE_SHARE_WRITE , // Permissões d
                            &fileHandlesSecurity , // Security, com herança de h
                            OPEN_EXISTING , // Abre um arquivo existente
                            FILE_ATTRIBUTE_NORMAL , // Atributos do arquivo
                            NULL // Template
                          ) ;
      if( hInFile == INVALID_HANDLE_VALUE )
      {
         return "Erro ao abrir arquivo de Input para o comando."
                             " Comando Abortado.\0" ;
      }
   }

   if ( ( outFileName != NULL ) && ( strlen( outFileName ) > 0 ) ) {
      hOutFile = CreateFile( outFileName , // Nome do arquivo
                            FILE_WRITE_DATA | FILE_READ_DATA , // Permissões de 
                            FILE_SHARE_READ | FILE_SHARE_WRITE , // Permissões d
                            &fileHandlesSecurity , // Security, com herança de h
                            CREATE_ALWAYS , // Abre um arquivo existente
                            FILE_ATTRIBUTE_NORMAL , // Atributos do arquivo
                            NULL // Template
                          ) ;
      if( hOutFile == INVALID_HANDLE_VALUE )
      {
         return "Erro ao abrir arquivo de Output para o comando."
                             " Comando Abortado.\0" ;
      }
   }
   
   if ( ( errorFileName != NULL ) && ( strlen( errorFileName ) > 0 ) ) {
      if ( ( outFileName == NULL ) ||
        (strcmp( errorFileName , outFileName ) != 0 ))
        {
        hErrorFile = CreateFile( errorFileName , // Nome do arquivo
                            FILE_WRITE_DATA | FILE_READ_DATA , // Permissões de 
                            FILE_SHARE_READ | FILE_SHARE_WRITE , // Permissões d
                            &fileHandlesSecurity , // Security, com herança de h
                            CREATE_ALWAYS , // Abre um arquivo existente
                            FILE_ATTRIBUTE_NORMAL , // Atributos do arquivo
                            NULL // Template
                          ) ;
hErrorFile = INVALID_HANDLE_VALUE;
        if( hErrorFile == INVALID_HANDLE_VALUE )
        {     
          return "Erro ao abrir arquivo de Error para o comando."
                             " Comando Abortado.\0" ;
        }
      }
      else {
        hErrorFile = hOutFile ;
      }
   }

   si->hStdOutput = hOutFile ;
   si->hStdInput = hInFile ;
   si->hStdError = hErrorFile ;
   return NULL;
}

/******************************************************************
 *
 *   Função _luaCreateProcess
 *    Função utilizada para recuperar o status de um processo cujo
 *    o handle é passado na pilha como parâmetro. Esta  função é
 *    registrada como executecommand na tabela servermanager,
 *    global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - id:
 *   Tipo: string
 *   Descrição: Esta função pega na pilha de LUA a string que 
 *              identifica o comando a ser executado
 * - command:
 *   Tipo: string
 *   Descrição: Esta função pega na pilha de LUA a linha de
 *              comando a ser executada.
 * - infile:
 *   Tipo: string
 *   Descrição: Esta função pega na pilha de LUA o nome do arquivo
 *              de input para o processo. Caso este arquivo seja
 *              inexistente, o processo não é disparado e este erro
 *              é retornado na pilha.
 * - outfile:
 *   Tipo: string
 *   Descrição: Esta função pega na pilha de LUA o nome do arquivo
 *              de output para o processo. Caso este arquivo exista,
 *              a função sobrescreve este arquivo. Caso não consiga
 *              criar o arquivo, este erro é retornado na pilha.
 * - errorfile:
 *   Tipo: string
 *   Descrição: Esta função pega na pilha de LUA o nome do arquivo
 *              de output de erro para o processo. Caso este arquivo
 *              exista, a função sobrescreve este arquivo. Caso não
 *              consiga criar o arquivo, este erro é retornado na pilha.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o status do comando. Este status pode possuir o valor RUNNING
 *    ou FINISHED. Em caso de erro, o valor retornado é nil e é
 *    inserida na pilha a descrição do erro. Em caso de sucesso o
 *    valor de erro nil é inserido na pilha.
 *
 ******************************************************************/

static int _luaCreateProcess( lua_State * L )
{
   char* BashPattern;
   char* BashCommandLine;
   LPCTSTR CommandId = NULL ;
   LPCTSTR CommandLine = NULL ;
   LPCTSTR inFileName = NULL ;
   LPCTSTR outFileName = NULL ;
   LPCTSTR errorFileName = NULL  ;
   LPCTSTR Error = NULL ;
   HANDLE hName = NULL ;
   BOOL defFiles = FALSE ;
   int  countFiles = 0;

   // Settings de segurança para redirecionamento de I/O
   SECURITY_ATTRIBUTES processHandlesSecurity ;
   processHandlesSecurity.bInheritHandle = TRUE ;
   processHandlesSecurity.lpSecurityDescriptor = NULL ;
   processHandlesSecurity.nLength = sizeof( SECURITY_ATTRIBUTES ) ;

   SECURITY_ATTRIBUTES threadHandlesSecurity ;
   threadHandlesSecurity.bInheritHandle = TRUE ;
   threadHandlesSecurity.lpSecurityDescriptor = NULL ;
   threadHandlesSecurity.nLength = sizeof( SECURITY_ATTRIBUTES ) ;

   if ( lua_isstring( L , 1 ) )
   {
      CommandId = lua_tostring( L , 1 ) ;
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Identificador de comando não fornecido.\0" ) ;
      return 2 ;
   }
   if ( lua_isstring( L , 2 ) )
   {
      CommandLine = lua_tostring( L , 2 ) ;
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Linha de comando não fornecida.\0" ) ;
      return 2 ;
   }
   if ( lua_isstring( L , 3 ) )
   {
      countFiles++;
      inFileName = lua_tostring( L , 3 ) ;
   }
   if ( lua_isstring( L , 4 ) )
   {
      countFiles++;
      outFileName = lua_tostring( L , 4 ) ;
      if (countFiles == 1) 
        inFileName = "\0";
   }
   else if (countFiles == 1) {
      outFileName = "\0";
   }
   if ( lua_isstring( L , 5) )
   {
      countFiles++;
      errorFileName = lua_tostring( L , 5) ;
      if (countFiles == 1) {
        inFileName = "\0";
        outFileName = "\0";
      }
   }
   else if (countFiles > 0) {
      errorFileName = "\0";
   }
   if ( lua_isstring( L , 6) )
   {
      hName = (HANDLE) lua_tostring( L , 6) ;
   }

   STARTUPINFO si;
   PROCESS_INFORMATION pi;
   ZeroMemory(&si, sizeof(STARTUPINFO));
   si.cb = sizeof(STARTUPINFO);

   if ( ( countFiles > 0 ) && ( countFiles < 3 ) ) {
     /* Não é possível trocar apenas um dos handles default, quando
      * queremos abrir um novo console para a execução do processo que
      * será criado. Por isso, precisamos criar um processo auxiliar
      * que abre a nova console e cria então o processo que realmente
      * desejamos, redefinindo apenas alguns handles dessa nova console.
      */
     BashPattern = "startcmd '%s' '%s' '%s' '%s'" ;
     BashCommandLine = (char*) malloc( sizeof(char) * (strlen(BashPattern) +
         strlen(CommandLine) + strlen(inFileName) + strlen(outFileName) +
         strlen(errorFileName) + 1));
     if (BashCommandLine == NULL) {
       lua_pushnil( L ) ;
       lua_pushstring( L , "Erro ao criar commando para execução.\0" ) ;
       return 2 ;
     }
     sprintf(BashCommandLine, BashPattern, CommandLine, inFileName,
       outFileName, errorFileName);
   }
   else {
     /* Podemos disparar o processo que desejamos diretamente, sem
      * redefinir nenhum handle ou redefinindo os três.
      */
     BashPattern = "bash -c '%s'" ;
     BashCommandLine = (char*) malloc( sizeof(char) * (strlen(BashPattern) +
                                     strlen(CommandLine) + 1));
     if (BashCommandLine == NULL) {
       lua_pushnil( L ) ;
       lua_pushstring( L , "Erro ao criar commando para execução.\0" ) ;
       return 2 ;
     }
     sprintf(BashCommandLine, BashPattern, CommandLine);

     if ( countFiles == 3 ) {
       defFiles = TRUE ;
       si.dwFlags = STARTF_USESTDHANDLES ;
       Error = setStartupHandles( &si, inFileName, outFileName, errorFileName);
       if ( Error ) {
         lua_pushnil( L ) ;
         lua_pushstring( L , Error) ;
         return 2 ;
       }
     }
   }

   if ( CreateProcess(  NULL , // Application name, não condiz com linha de comando
                        (LPSTR)BashCommandLine , // Linha de comando a ser executada
                        &processHandlesSecurity , // Com herança de handles de processo
                        &threadHandlesSecurity  , // Com herança de handles de Threads
                        defFiles , // Com herança de handles "herdáveis"
                        CREATE_NEW_CONSOLE , // Flags de criação
                        NULL , // Flags de Ambiente
                        NULL , // Diretório da aplicação
                        &si , // Dados para inicialização de processos
                        &pi // Estrutura a ser preenchida com handles e ids do processo e main thread
                     ) == TRUE )
   {
      // Onde devemos fazer o free??
      free(BashCommandLine);
      // REFRESH!!
      if ( RefreshAllProcessesInfo( L ) == 0 )
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Falha ao atualizar dados de processos.\0" ) ;
         return 2 ;
      }
      PCOMMAND ProcessData = ( PCOMMAND ) malloc( sizeof( COMMAND ) ) ; // Desaloca em releasecommandinfo
      if (ProcessData == NULL) {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Sem memória para criar handle" ) ;
         return 2 ;
      }
      ProcessData->hStdIn = si.hStdInput ;
      ProcessData->hStdOut = si.hStdOutput ;
      ProcessData->hStdError = si.hStdError ;
      ProcessData->hProcess = pi.hProcess ;
      ProcessData->hName = hName ;
      ProcessData->CommandId = CommandId ;
      ProcessData->pid = pi.dwProcessId ;
      lua_pushlightuserdata( L , ( void * ) ProcessData ) ;
      lua_pushnil( L ) ;
      return 2 ;
   }
   else
   {
      // Onde devemos fazer o free??
      free(BashCommandLine);
      lua_pushnil( L ) ;
      lua_pushstring( L , "Erro ao criar processo\0" ) ;
      return 2 ;
   }
}

/******************************************************************
 *
 *   Função _getCommandId
 *    Função utilizada para recuperar o ID de um comando
 *    disparado pelo programa. Esta  função é registrada como
 *    getcommandid na tabela servermanager, global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o ID do processo em questão.
 *
 ******************************************************************/

static int _getCommandId( lua_State * L )
{
   if ( lua_isuserdata( L, 1 ) )
   {

      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , 1 ) ;
      if (CommandHandle->CommandId == NULL) {
        lua_pushnil( L );
        lua_pushstring( L , "Id de comando inválido (nulo).\0" ) ;
      }
      else {
        lua_pushstring( L , CommandHandle->CommandId);
        lua_pushnil( L ) ;
      }

      return 2 ;
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Handle de comando inválido.\0" ) ;
      return 2 ;
   }
}

/******************************************************************
 *
 *   Função _getProcessPid
 *    Função utilizada para recuperar o PID de uma processo
 *    disparado pelo programa. Esta  função é registrada como
 *    getcommandpid na tabela servermanager, global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    o PID do processo em questão.
 *
 ******************************************************************/

static int _getProcessPid( lua_State * L )
{
   if ( lua_isuserdata( L, 1 ) )
   {

      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , 1 ) ;
      lua_pushnumber( L , CommandHandle->pid);
      lua_pushnil( L ) ;
      return 2 ;
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Handle de comando inválido.\0" ) ;
      return 2 ;
   }
}


/******************************************************************
 *
 *   Função _kill
 *    Função utilizada para recuperar o status de um processo cujo
 *    o handle é passado na pilha como parâmetro. Esta  função é
 *    registrada como killcommand na tabela servermanager, global
 *    no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 *
 *   Valor de retorno:
 *   -  Em caso de sucesso, retorna ok = 1 e error = nil. Em caso de
 *    erro retorna ok = nil e error = descrição do erro na função.
 *
 ******************************************************************/

static int _kill( lua_State *L )
{
   DWORD* procs = NULL;
   unsigned long nProcs = 0 ;
   unsigned long i = 0;
   BOOL retVal = TRUE ;
   HANDLE hProcess ;

   if ( lua_isuserdata( L, 1 ) )
   {
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , 1 ) ;

      if ( RefreshAllProcessesInfo( L ) == 0 )
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Falha ao atualizar dados de processos.\0" ) ;
         return 2 ;
      }

      // Finaliza os processos
      if( _getProcessesByPID( L, CommandHandle->pid, &procs, &nProcs ) ) {
         for( i = 0 ; i < nProcs ; i++ ) {
            hProcess = OpenProcess( PROCESS_ALL_ACCESS , FALSE , procs[i] );
            retVal = retVal & TerminateProcess( hProcess , 0 ); //verdadeiro caso tenha sucesso
            CloseHandle( hProcess );
         }

         if( nProcs > 0 ) {
            free( procs );
         }
         
         if( retVal ) {
            lua_pushboolean( L, 1 );
            return 1;
         }
      }

      lua_pushnil( L );
      lua_pushstring( L , "Falha ao finalizar um ou mais processos." );
      return 2;
   }

   lua_pushnil( L );
   lua_pushstring( L , "Handle de comando inválido." );
   return 2 ;
}

/******************************************************************
 *
 *   Função _releasecommandinfo
 *    Função utilizada para liberar recursos alocados no disparo
 *    de um comando. Esta  função é registrada como
 *    releasecommandinfo na tabela servermanager, global no ambiente
 *    LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - Handle:
 *   Tipo: userdata
 *   Descrição: Esta função pega na pilha de LUA o Handle para o
 *              comando que é o mesmo criado quando o processo é
 *              disparado.
 *
 *   Valor de retorno:
 *   -  Em caso de sucesso, retorna ok = 1 e error = nil. Em caso de
 *    erro retorna ok = nil e error = descrição do erro na função.
 *
 ******************************************************************/

static int _releasecommandinfo( lua_State * L )
{
   if ( lua_isuserdata( L, -1 ) )
   {
      PCOMMAND CommandHandle = ( PCOMMAND ) lua_touserdata( L , -1 ) ;
      if( CommandHandle )
      {
         CloseHandle( CommandHandle->hProcess ) ;
         if ( CommandHandle->hStdIn != NULL ) {
           CloseHandle( CommandHandle->hStdIn ) ;
         }
         if ( CommandHandle->hStdOut != NULL ) {
           CloseHandle( CommandHandle->hStdOut ) ;
         }
         if ( CommandHandle->hStdError != NULL ) {
           CloseHandle( CommandHandle->hStdError ) ;
         }
         free( CommandHandle ) ;
         lua_pushnumber( L , 1.0 ) ;
         lua_pushnil( L ) ;
         return 2 ;
      }
      else
      {
         lua_pushnil( L ) ;
         lua_pushstring( L , "Handle de comando inválido\0" ) ;
         return 2 ;
      }
   }
   else
   {
      lua_pushnil( L ) ;
      lua_pushstring( L , "Handle de comando inválido\0" ) ;
      return 2 ;
   }
}

/*****************************************************************
 *
 * Funções utilizadas para monitorar desempenho da máquina.
 *
 *****************************************************************/

/******************************************************************
 *
 *   Função _getCpuUsage
 *    Função utilizada para recuperar o uso de CPU entre 2 chamadas
 *    à esta função. Caso o intervalo de tempo seja menor do que 10s
 *    a função não recalcula o valor e retorna novamente o uso de
 *    CPU calculado anteriormente. Esta  função é registrada como
 *    getcpuload na tabela servermanager, global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - MaxOld:
 *   Tipo: number
 *   Descrição: Tempo máximo que a informação pode ter.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    a carga de cpu na máquina. Em caso de erro, o valor
 *    retornado é nil e é inserida na pilha a descrição do erro.
 *    Em caso de sucesso o valor de erro nil é inserido na pilha.
 *
 ******************************************************************/

static int _getCpuUsage( lua_State * L )
{
   static bool bFirstTime = true;
   static DWORD lnOldValue = 0;
   static LARGE_INTEGER   OldPerfTime100nSec = {0};
   static PLATFORM Platform = GetPlatform();
   static double CpuUsage = 0.0 ;

   // Teste para checar se informação precisa ser atualizada
   double MaxOld = lua_tonumber( L , - 1 ) ;

   FILETIME now ;
   GetSystemTimeAsFileTime( &now ) ;

   LARGE_INTEGER aux ;
   aux.LowPart = now.dwLowDateTime ;
   aux.HighPart = now.dwHighDateTime ;

   double NewTimeStamp = ( double ) aux.QuadPart ;
   double CpuLoadAge = ( NewTimeStamp - ( ( double ) OldPerfTime100nSec.QuadPart ) ) / NS_TO_S ;

   if( CpuLoadAge <= MaxOld )
   {
      lua_pushnumber( L , CpuUsage ) ;
      lua_pushnil( L ) ;
      return 2 ;
   }

   PPERF_DATA_BLOCK PerfData = QueryPerformanceData( ) ;
   if (PerfData == NULL) {
      lua_pushnil( L ) ;
      lua_pushstring( L ,"Não foi possível obter dados sobre o processo.\0" ) ;
      return 2;
   }

   char szInstance[256] = {0};

   // Há uma diferença entre os contadores no Windows XP/2k e NT, segundo o artigo
   // Q259390 no MSDN. Por isso é necessário saber qual o sistema operacional.

   unsigned long dwObjectIndex;
   unsigned long dwCpuUsageIndex;
   switch (Platform)
   {
   case WINNT:
      dwObjectIndex = SYSTEM_OBJECT_INDEX;
      dwCpuUsageIndex = TOTAL_PROCESSOR_TIME_COUNTER_INDEX;
      break;
   case WIN2K_XP:
      dwObjectIndex = PROCESSOR_OBJECT_INDEX;
      dwCpuUsageIndex = PROCESSOR_TIME_COUNTER_INDEX;
      strcpy(szInstance,"_Total");
      break;
   default:
      free( PerfData ) ;
      lua_pushnil( L ) ;
      lua_pushstring( L ,"SGA sem suporte à plataforma.\0" ) ;
      return 2;
   }

   LARGE_INTEGER   NewPerfTime100nSec = {0} ;
   NewPerfTime100nSec = PerfData->PerfTime100nSec ;
   DWORD lnNewValue ;
   lnNewValue = GetCounterValue( PerfData, dwObjectIndex, dwCpuUsageIndex, szInstance ) ;

   // Teste para saber se é a primeira chamada à função
   if (bFirstTime)
   {
      bFirstTime = false;
      lnOldValue = lnNewValue;
      OldPerfTime100nSec = NewPerfTime100nSec;
      free(PerfData);
      lua_pushnil( L ) ;
      lua_pushstring( L ,"Mínimo de duas chamadas necessárias para cálculo. Próxima chamada retornará valor correto.\0" ) ;
      return 2;
   }

   double DeltaPerfTime100nSec = (double) ( NewPerfTime100nSec.QuadPart - OldPerfTime100nSec.QuadPart ) ;

   LONGLONG lnValueDelta = lnNewValue - lnOldValue ;

   lnOldValue = lnNewValue ;
   OldPerfTime100nSec = NewPerfTime100nSec ;

   CpuUsage = (double)lnValueDelta / DeltaPerfTime100nSec ;

   CpuUsage = ( 1.0 - CpuUsage ) * 100.0 ;
   if ( CpuUsage < 0.0 )
   {
      lua_pushnumber( L , 0 ) ;
      lua_pushnil( L ) ;
      free(PerfData) ;
      return 2 ;
   }
   else
   {
      lua_pushnumber( L , CpuUsage ) ;
      lua_pushnil( L ) ;
      free(PerfData) ;
      return 2 ;
   }
}

/******************************************************************
 *
 *   Função _getMemoryUsage
 *    Função utilizada para recuperar o uso de memória na máquina.
 *    Esta  função é registrada como getmemoryload na tabela
 *    servermanager, global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * - MaxOld:
 *   Tipo: number
 *   Descrição: Tempo máximo que a informação pode ter. Não é
 *              utilizado nesta função.
 *
 *   Valor de retorno:
 *   -  A função retorna, na pilha do state passado como parâmetro,
 *    uma tabela na qual o campo ram é o valor de memória física
 *    em uso e o campo swap é o tamanho de memória swap em uso.
 *    Também é inserido na pilha o valor nil, que é a descrição do
 *    erro. A API do Windows não retorna erros para esta função.
 *
 ******************************************************************/

static int _getMemoryUsage( lua_State * L )
{
   MEMORYSTATUSEX MemStatus ;
   MemStatus.dwLength = sizeof(MemStatus);
   GlobalMemoryStatusEx( &MemStatus ) ;
   DWORDLONG PhysicalMem = MemStatus.ullTotalPhys - MemStatus.ullAvailPhys ;
   DWORDLONG SwapMem = MemStatus.ullTotalPageFile - MemStatus.ullAvailPageFile ;
   lua_newtable( L ) ;
   lua_pushstring( L , "swap\0" ) ;
   lua_pushnumber( L , (lua_Number) SwapMem ) ;
   lua_rawset( L , - 3 ) ;
   lua_pushstring( L , "ram\0" ) ;
   lua_pushnumber( L , (lua_Number) PhysicalMem ) ;
   lua_rawset( L , - 3 ) ;
   lua_pushnil( L ) ;
   return 2 ;
}

/******************************************************************
 *
 *   Função _sleep
 *    Função utilizada para fazer o sga dormir. Esta  função é
 *    registrada como getmemoryload na tabela servermanager, global
 *    no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 *
 *   Valor de retorno:
 *   -  A função não retorna valores.
 *
 ******************************************************************/

static int _sleep( lua_State * L )
{
  double dtime = lua_tonumber(L, -1 );
  DWORD time = (DWORD) (dtime * 1000);
  Sleep( time );
  return 0;
}

/******************************************************************
 *
 *   Função _now
 *    Função utilizada para saber data do sistema, em segundos.
 *    Esta  função é registrada como now na tabela servermanager,
 *    global no ambiente LUA.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 *
 *   Valor de retorno:
 *   -  Retorna number contendo a data do sistema em segundos.
 *
 ******************************************************************/

static int _now( lua_State * L )
{
   FILETIME now ;
   GetSystemTimeAsFileTime( &now ) ;

   LARGE_INTEGER aux ;
   aux.LowPart = now.dwLowDateTime ;
   aux.HighPart = now.dwHighDateTime ;

   double DateInSeconds = ( double ) aux.QuadPart / NS_TO_S ;
   lua_pushnumber( L , DateInSeconds ) ;
   return 1 ;
}


/**
 * Lê e executa um script Lua.
 * 
 * Retorno:
 * 0 : Sucesso
 * 1 : Erro
 */
static int _dofile(lua_State* L, char* file)
{
  if (luaL_loadfile(L, file) || lua_pcall(L, 0, 0, 0)) {
    int n = lua_gettop(L);
    const char *msg = lua_tostring (L, n);
    lua_pop(L, 1);
    fprintf(stderr, "%s\n", msg);

    return 1;
  }

  return 0;
}

/**
 * Função _setHistoric
 *  Configura a variável que alimenta o histórico de execução dos algoritmos.
 * 
 * Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 * 
 * Valor de retorno:
 *  0 : Sucesso
 *  1 : Erro
 */
static int _setHistoric( lua_State * L )
{
  enableHistoric = lua_toboolean(L, -1 );
  
  return 1;
}

/**
 * Função _getHistoric
 *  Obtém a variável que alimenta o histórico de execução dos algoritmos.
 * 
 * Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: Como esta função é exportada para lua na tabela
 *               severmanager, é necessário saber qual o State.
 *
 * Valor de retorno:
 * 0 : Sucesso
 * 1 : Erro
 */
static int _getHistoric( lua_State * L )
{
   // Exportação para a pilha de LUA
   lua_pushboolean( L , enableHistoric ) ;
   
   return 1 ;
}

/******************************************************************
 *
 *   Função luaopen_sga
 *    Função utilizada para registrar a tabela servermanager.
 *
 *   Parâmetros:
 * -  L:
 *    Tipo: lua_State
 *    Descrição: State no qual a tabela será registrada.
 *
 *   Valor de retorno:
 *   -  A função não retorna valores.
 *
 ******************************************************************/

int luaopen_sga( lua_State * L )
{
   // Inicializa Controle de processos
   Processes.TimeStamp = 0 ;
   Processes.currCPU = &Processes.cpu1;
   Processes.oldCPU = &Processes.cpu2;

   // Guarda estado da pilha para restaurá-la
   int StackState = lua_gettop( L ) ;

   // Cria uma tabela para conter as funções
   lua_newtable( L ) ;

   // Adiciona os métodos a esta tabela
   lua_pushstring( L , "getnumcpus\0" ) ;
   lua_pushcfunction( L , _getNumberOfCpus ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getmemory\0" ) ;
   lua_pushcfunction( L , _getStaticMemoryInfo ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getbyteorder\0" ) ;
   lua_pushcfunction( L , _getByteOrderType ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcpuload\0" ) ;
   lua_pushcfunction( L , _getCpuUsage ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getmemoryload\0" ) ;
   lua_pushcfunction( L , _getMemoryUsage ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "executecommand\0" ) ;
   lua_pushcfunction( L , _luaCreateProcess ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcommandpersistentdata\0" ) ;
   lua_pushcfunction( L , _getcommandpersistentdata ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcommandPPID\0" ) ;
   lua_pushcfunction( L , _getProcessParentPid ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "retrievecommandhandle\0" ) ;
   lua_pushcfunction( L , _retrievecommandhandle ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcommandstatus\0" ) ;
   lua_pushcfunction( L , _getProcessStatus ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcommandexechost\0" ) ;
   lua_pushcfunction( L , _getProcessExecHost ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcommandcpuload\0" ) ;
   lua_pushcfunction( L , _getProcessCpuLoad ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcommandmemoryload\0" ) ;
   lua_pushcfunction( L , _getProcessMemoryLoad ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcommandtimeusage\0" ) ;
   lua_pushcfunction( L , _getProcessTimes ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcommandid\0" ) ;
   lua_pushcfunction( L , _getCommandId ) ;
   lua_rawset( L , - 3 ) ;

   lua_pushstring( L , "getcommandpid\0" ) ;
   lua_pushcfunction( L , _getProcessPid ) ;
   lua_rawset( L , - 3 ) ;

   lua_pushstring( L , "killcommand\0" ) ;
   lua_pushcfunction( L , _kill ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "releasecommandinfo\0" ) ;
   lua_pushcfunction( L , _releasecommandinfo ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getcommandallinfo\0" ) ;
   lua_pushcfunction( L , _getCommandAllInfo ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getjobsinfo\0" ) ;
   lua_pushcfunction( L , _getJobsInfo ) ;
   lua_rawset( L , -3 ) ;
 
   lua_pushstring( L , "getnumberofjobs\0" ) ;
   lua_pushcfunction( L , _getNumberOfJobs ) ;
   lua_rawset( L , -3 ) ;
 
   lua_pushstring( L , "sleep\0" ) ;
   lua_pushcfunction( L , _sleep ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "now\0" ) ;
   lua_pushcfunction( L , _now ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "open\0" ) ;
   lua_pushcfunction( L , _open ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "close\0" ) ;
   lua_pushcfunction( L , _close ) ;
   lua_rawset( L , -3 ) ;
   
   lua_pushstring( L , "setHistoric\0" ) ;
   lua_pushcfunction( L , _setHistoric ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "getHistoric\0" ) ;
   lua_pushcfunction( L , _getHistoric ) ;
   lua_rawset( L , -3 ) ;

   // Adiciona as constantes a esta tabela
   lua_pushstring( L , "RUNNING\0" ) ;
   lua_pushnumber( L , RUNNING ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "NOT_RESPONDING\0" ) ;
   lua_pushnumber( L , NOT_RESPONDING ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "WAITING\0" ) ;
   lua_pushnumber( L , WAITING ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "FINISHED\0" ) ;
   lua_pushnumber( L , FINISHED ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "LITTLE_ENDIAN\0" ) ;
   lua_pushnumber( L , LITTLE_ENDIAN ) ;
   lua_rawset( L , -3 ) ;

   lua_pushstring( L , "BIG_ENDIAN\0" ) ;
   lua_pushnumber( L , BIG_ENDIAN ) ;
   lua_rawset( L , -3 ) ; 

   // Pega a tabela da pilha, já preenchida, e a adiciona
   // à tabela de globais sobre o nome de servermanager.
   lua_setglobal( L , "servermanager\0" ) ;

   // Ajuste de uma variável global para ajustar o pid do próprio sga-daemon
   int sga_pid = GetCurrentProcessId();
   lua_pushnumber(L, sga_pid);
   lua_setglobal(L, "SGA_PID\0");

   // Salva o número de CPUs
   SYSTEM_INFO SysInfo;
   GetSystemInfo( &SysInfo );
   numberOfCpus = SysInfo.dwNumberOfProcessors;

   // Carrega funções auxiliares
   _dofile(L, "lib/Windows.lua");

   // Restaura estado da pilha
   lua_settop( L , StackState ) ;

   return 0;
}
