/*======================================================================================//
|                                                                                       //
|Copyright (c) 1998, 1999 Sequent Computer Systems, Incorporated.  All rights reserved. //
|                                                                                       //
|Description:                                                                           //
|                                                                                       //
|---------------------------------------------------------------------------------------//            
|   This file implements the CProcCon class methods defined in ProcConSvc.h             //
|---------------------------------------------------------------------------------------//            
|                                                                                       //
|Created:                                                                               //
|                                                                                       //
|   Jarl McDonald 07-98                                                                 //
|                                                                                       //
|Revision History:                                                                      //
|                                                                                       //
|=======================================================================================*/
#include "ProcConSvc.h"

// CProcCon Constructor
// Note: this function runs as part of service start so keep it quick!
CProcCon::CProcCon( void ) : m_ready( FALSE ), m_shutEvent( NULL ), m_versionInfo( NULL ),
                             m_endEvent ( NULL ),
                             m_shutDown( FALSE )
{
   memset( &m_context, 0, sizeof(m_context) );

   m_hThread[USER_SERVER] = m_hThread[PROC_SERVER] = NULL;
   SYSTEM_INFO si;
   GetSystemInfo( &si );
   m_NumberOfProcessors = si.dwNumberOfProcessors;
   m_PageSize           = si.dwPageSize;

   m_versionInfo = new CVersion( GetModuleHandle( NULL ) );

   PCBuildAdminSecAttr( m_secAttr ); 

   m_context.cDB = new CProcConDB( m_PageSize );

   if ( !m_context.cDB || !m_context.cDB->ReadyToRun() || 
        !m_versionInfo || !m_secAttr.lpSecurityDescriptor ) {
      PCLogMessage( PC_STARTUP_FAILED, EVENTLOG_ERROR_TYPE, 1, PROCCON_SVC_DISP_NAME );
      return;
   }

   m_shutEvent             = CreateEvent( NULL,       TRUE,  FALSE, NULL );
   m_endEvent              = CreateEvent( NULL,       TRUE,  FALSE, NULL );
   m_context.mgrDoneEvent  = CreateEvent( NULL,       TRUE,  FALSE, NULL );
   m_context.userDoneEvent = CreateEvent( NULL,       TRUE,  FALSE, NULL );
   m_context.mediatorEvent = CreateEvent( &m_secAttr, FALSE, FALSE, PC_MEDIATOR_EVENT );
   if ( !m_shutEvent || !m_endEvent || !m_context.mgrDoneEvent || !m_context.userDoneEvent || !m_context.mediatorEvent ) {
      PCLogUnExError( m_context.mediatorEvent? TEXT("PCEvent") : PC_MEDIATOR_EVENT, TEXT("CreateEvent") );
      return;
   }

   // Allocate shared memory for mediator view of jobs...
   m_context.mediatorTableHandle = CreateFileMapping( HANDLE_FF_64, &m_secAttr, PAGE_READWRITE, 
                                                      0, sizeof(PCMediateHdr), PC_MEDIATOR_FILEMAP );
   if ( !m_context.mediatorTableHandle ) {
      PCLogUnExError( PC_MEDIATOR_FILEMAP, TEXT("CreateMediatorMapping") );
      return;
   }
   m_context.mediatorTable = (PCMediateHdr *) MapViewOfFile( m_context.mediatorTableHandle, 
                                                             FILE_MAP_WRITE, 0, 0, 0 );
   if ( !m_context.mediatorTable ) {
      CloseHandle( m_context.mediatorTableHandle );
      m_context.mediatorTableHandle = NULL;
      PCLogUnExError( PC_MEDIATOR_FILEMAP, TEXT("MapMediatorJobData") );
      return;
   }

   m_context.mediatorTable->svcEventHandle = m_context.mediatorEvent;
   m_context.mediatorTable->svcPID         = GetCurrentProcessId();

   // If mediator is not running, init shared memory, set up completion port, and start mediator...
   if ( !PCTestIsRunning( PC_MEDIATOR_EXCLUSION ) ) {
      memset( &m_context.mediatorTable->groupBlock, 0, sizeof(m_context.mediatorTable->groupBlock) );
      m_context.mediatorTable->lastCompKey = 0;
      m_context.completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, NULL, 0 );
      if ( !m_context.completionPort )
         PCLogUnExError( TEXT("PCInit"), TEXT("CreateIoCompletionPort") );

      m_context.mediatorTable->svcPortHandle = m_context.completionPort;

      StartMediator();
   }
   // If mediator is already running... 
   // recover our completion port by duplicating the handle... 
   else {
      // Open mediator process so we can duplicate handles... 
      m_context.mediatorTable->medProcessInfo.hProcess = 
         OpenProcess( PROCESS_DUP_HANDLE, FALSE, m_context.mediatorTable->medProcessInfo.dwProcessId );
      // Recover our completion port by duplicating the handle... 
      if ( !DuplicateHandle( m_context.mediatorTable->medProcessInfo.hProcess,
                             m_context.mediatorTable->medPortHandle,
                             GetCurrentProcess(),
                             &m_context.mediatorTable->svcPortHandle,
                             NULL,
                             FALSE,
                             DUPLICATE_SAME_ACCESS ) )
         PCLogUnExError( TEXT("PCInit"), TEXT("DupMediatorPortHandle") );
      m_context.completionPort = m_context.mediatorTable->svcPortHandle;
      // Map all job table blocks by duplicating the handles...
      m_context.mediatorTable->SvcChainBlocks();
   }

   CloseHandle( m_context.mediatorTable->medProcessInfo.hProcess );

   m_context.cPC = this;
   LaunchProcServer();
   LaunchUserServer();

   if (!ReadyToRun() )
      HardStop();
}

// CProcCon Destructor
CProcCon::~CProcCon( void ) 
{
   if ( m_context.cUser )  { 
      WaitForSingleObject( m_context.userDoneEvent, 5000 );
      delete m_context.cUser;
      m_context.cUser = NULL; 
   }
   if ( m_context.cMgr  )  { 
      WaitForSingleObject( m_context.mgrDoneEvent, 5000 );
      delete m_context.cMgr;      
      m_context.cMgr  = NULL; 
   }
   if ( m_context.cDB   )  { 
      delete m_context.cDB;       
      m_context.cDB   = NULL; 
   }
   if ( m_shutEvent )      { 
      CloseHandle( m_shutEvent ); 
      m_shutEvent     = NULL; 
   }
   if ( m_endEvent )      { 
      CloseHandle( m_endEvent ); 
      m_endEvent      = NULL; 
   }
   if ( m_versionInfo )    { 
      delete m_versionInfo; 
      m_versionInfo   = NULL; 
   }

   CloseHandle( m_context.mgrDoneEvent        );
   CloseHandle( m_context.userDoneEvent       );
   CloseHandle( m_context.mediatorEvent       );
   CloseHandle( m_context.mediatorTableHandle );

   PCFreeSecAttr( m_secAttr );
}

//--------------------------------------------------------------------------------------------//
// Function to determine if all CProcCon initial conditions have been met                     //
// Input:    None                                                                             //
// Returns:  TRUE if ready, FALSE if not                                                      //
//--------------------------------------------------------------------------------------------//
BOOL CProcCon::ReadyToRun( void ) 
{ 
   return m_context.cDB          && 
          m_hThread[PROC_SERVER] && 
          m_hThread[USER_SERVER] && 
          m_secAttr.lpSecurityDescriptor; 
}

//--------------------------------------------------------------------------------------------//
// Function to start the ProcCon Mediator Process                                             //
// Input:    None                                                                             //
// Returns:  NT or ProcCon Error code                                                         //
//--------------------------------------------------------------------------------------------//
PCULONG32 CProcCon::StartMediator( void )
{
      // Don't start if already running...
      if ( PCTestIsRunning( PC_MEDIATOR_EXCLUSION ) )
         return PCERROR_MEDIATOR_ALREADY_RUNNING;

      // Get our module name then replace base name with mediator base name...
      // (Thus mediator exe must be in the same location as the service exe).
      TCHAR path[MAX_PATH];
      if ( !GetModuleFileName( NULL, path, MAX_PATH ) ) {
         PCLogUnExError( TEXT("PCMediator"), TEXT("GetMediatorPath") );
         path[0] = 0;
      }
      for ( int i = _tcslen( path ); i; --i ) {
         if ( path[i] == TEXT('\\') ) {  path[i + 1] = 0;  break;  }
      }
      _tcscat( path, PC_MEDIATOR_BASE_NAME );

      // Start mediator process -- don't quit if this fails...
      STARTUPINFO strtInfo;
      memset( &strtInfo, 0, sizeof(strtInfo) );
      strtInfo.cb = sizeof(strtInfo);
      if ( !CreateProcess( path, NULL, &m_secAttr, &m_secAttr, FALSE, 
                           CREATE_NEW_PROCESS_GROUP + CREATE_NO_WINDOW, 
                           NULL, NULL, &strtInfo, &m_context.mediatorTable->medProcessInfo ) ) {
         DWORD rc = GetLastError();
         PCLogUnExError( TEXT("PCMediator"), TEXT("CreateMediator") );
         return rc;
      }

      return ERROR_SUCCESS;
}

//--------------------------------------------------------------------------------------------//
// Function to stop (kill) the ProcCon Mediator Process                                       //
// Input:    None                                                                             //
// Returns:  NT or ProcCon Error code                                                         //
//--------------------------------------------------------------------------------------------//
PCULONG32 CProcCon::StopMediator ( void )
{
   DWORD  rc    = ERROR_SUCCESS;
   HANDLE hProc = OpenProcess( PROCESS_TERMINATE, FALSE, m_context.mediatorTable->medProcessInfo.dwProcessId );

   m_context.mediatorTable->medProcessInfo.hProcess = hProc;
         
   if ( !hProc ) {
      rc = GetLastError();
      if (rc == ERROR_INVALID_PARAMETER) rc = PCERROR_MEDIATOR_NOT_RUNNING;
   }
   else if ( !TerminateProcess( m_context.mediatorTable->medProcessInfo.hProcess, PCERROR_KILLED_BY_REQUEST ) )
      rc = GetLastError();

   if ( hProc )
      CloseHandle( hProc );

   return rc;
}

//--------------------------------------------------------------------------------------------//
// Function to return system information                                                      //
// Input:    Buffer for information, locations for size and count (always 1)                  //
// Returns:  TRUE if shutdown requested, FALSE if not                                         //
//--------------------------------------------------------------------------------------------//
void CProcCon::GetPCSystemInfo( PCSystemInfo *data, PCINT16 *itemLen, PCINT16 *itemCount )
{
   *itemLen   = sizeof(PCSystemInfo);
   *itemCount = 1;
   memset( data, 0, *itemLen );

   _tcsncpy( data->fileVersion,       m_versionInfo->GetFileVersion(),    VERSION_STRING_LEN );
   _tcsncpy( data->productVersion,    m_versionInfo->GetProductVersion(), VERSION_STRING_LEN );
   _tcsncpy( data->fileFlags,         m_versionInfo->GetFileFlags(),      VERSION_STRING_LEN );

   _tcsncpy( data->medFileVersion,    m_context.mediatorTable->medFileVersion,    VERSION_STRING_LEN );
   _tcsncpy( data->medProductVersion, m_context.mediatorTable->medProductVersion, VERSION_STRING_LEN );
   _tcsncpy( data->medFileFlags,      m_context.mediatorTable->medFileFlags,      VERSION_STRING_LEN );

   data->fixedSignature        = m_versionInfo->GetFixedSignature();     
   data->fixedFileVersionMS    = m_versionInfo->GetFixedFileVersionMS(); 
   data->fixedFileVersionLS    = m_versionInfo->GetFixedFileVersionLS();     
   data->fixedProductVersionMS = m_versionInfo->GetFixedProductVersionMS(); 
   data->fixedProductVersionLS = m_versionInfo->GetFixedProductVersionLS();     
   data->fixedFileFlags        = m_versionInfo->GetFixedFileFlags(); 
   data->fixedFileOS           = m_versionInfo->GetFixedFileOS();     
   data->fixedFileType         = m_versionInfo->GetFixedFileType();     
   data->fixedFileSubtype      = m_versionInfo->GetFixedFileSubtype(); 
   data->fixedFileDateMS       = m_versionInfo->GetFixedFileDateMS();     
   data->fixedFileDateLS       = m_versionInfo->GetFixedFileDateLS();

   data->sysParms.manageIntervalSeconds = m_context.cDB->GetPollDelaySeconds();
   data->sysParms.timeoutValueMs        = m_context.cUser->GetTimeout();
   data->sysParms.numberOfProcessors    = m_NumberOfProcessors;
   data->sysParms.memoryPageSize        = m_PageSize;
   data->sysParms.processorMask         = m_context.cMgr->GetSystemMask();
}

//--------------------------------------------------------------------------------------------//
// CProcCon thread function -- this function runs in its own thread                           //
// Input:    None                                                                             //
// Returns:  Nothing                                                                          //
// Note:     All ProcCon work is done in the user communication and process management        //
//           threads so this fcn has only an oversight role.                                  //
//--------------------------------------------------------------------------------------------//
void CProcCon::Run( void ) 
{
  ResumeThread(m_hThread[USER_SERVER] );
  ResumeThread(m_hThread[PROC_SERVER] );

  WaitForSingleObject( m_endEvent, INFINITE );
}

//--------------------------------------------------------------------------------------------//
// CProcCon function to handle 'hard' stop: failure before threads are released               //
// Input:    Optional Thread Exit Code                                                        //
// Returns:  Nothing                                                                          //
// Note:     This function forcefully close the user communication and process management     //
//           threads.                                                                         //
//--------------------------------------------------------------------------------------------//
void CProcCon::HardStop( PCULONG32 ExitCode ) 
{
  if ( m_hThread[USER_SERVER] ) {
    TerminateThread(m_hThread[USER_SERVER], ExitCode );
    m_hThread[USER_SERVER] = NULL;
  }
  if ( m_hThread[PROC_SERVER] ) { 
    TerminateThread(m_hThread[PROC_SERVER], ExitCode );
    m_hThread[PROC_SERVER] = NULL;
  }
}

//--------------------------------------------------------------------------------------------//
// Function called by service stop when stop requested                                        //
// Input:    None                                                                             //
// Returns:  Nothing                                                                          //
// Note:     this function runs as part of service stop                                       //
//--------------------------------------------------------------------------------------------//
void CProcCon::Stop( void ) 
{
   if ( m_shutEvent ) {
      m_shutDown = TRUE;
      SetEvent( m_shutEvent );
      WaitForMultipleObjects( ENTRY_COUNT(m_hThread), m_hThread, TRUE, 30000 );
      SetEvent( m_endEvent );
   }
}

//--------------------------------------------------------------------------------------------//
// Function to start the Process Management thread                                            //
// Input:    None                                                                             //
// Returns:  Nothing                                                                          //
// Note:     this function runs as part of service start so it must be quick.                 //
//--------------------------------------------------------------------------------------------//
void CProcCon::LaunchProcServer( void )                     
{
   m_hThread[PROC_SERVER] = CreateThread( NULL, 0, &PCProcServer, &m_context, CREATE_SUSPENDED, NULL );
   if ( !m_hThread[PROC_SERVER] )
      PCLogUnExError( TEXT("PCProcServer"), TEXT("CreateThread") );
}

//--------------------------------------------------------------------------------------------//
// Function to start the User Communication thread                                            //
// Input:    None                                                                             //
// Returns:  Nothing                                                                          //
// Note:     this function runs as part of service start so it must be quick.                 //
//--------------------------------------------------------------------------------------------//
void CProcCon::LaunchUserServer( void )                     
{
   m_hThread[USER_SERVER] = CreateThread( NULL, 0, &PCUserServer, &m_context, CREATE_SUSPENDED, NULL );
   if ( !m_hThread[USER_SERVER] )
      PCLogUnExError( TEXT("PCUserServer"), TEXT("CreateThread") );
}

// End of CProcCon.cpp
//============================================================================J McDonald fecit====//