/* ------------------------------------------------------------------------
   Windows NT Service class library
   Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
   This file is public domain and comes with NO WARRANTY of any kind
 -------------------------------------------------------------------------- */
#include <windows.h>
#include <process.h>
#include "nt_servc.h"


static NTService *pService;

/* ------------------------------------------------------------------------

 -------------------------------------------------------------------------- */
NTService::NTService()
{

    bOsNT	     = FALSE;
    //service variables
    ServiceName      = NULL;
    hExitEvent	     = 0;
    bPause	     = FALSE;
    bRunning	     = FALSE;
    hThreadHandle    = 0;
    fpServiceThread  = NULL;

    //time-out variables
    nStartTimeOut    = 15000;
    nStopTimeOut     = 15000;
    nPauseTimeOut    = 5000;
    nResumeTimeOut   = 5000;

    //install variables
    dwDesiredAccess  = SERVICE_ALL_ACCESS;
    dwServiceType    = SERVICE_WIN32_OWN_PROCESS;
    dwStartType      = SERVICE_AUTO_START;
    dwErrorControl   = SERVICE_ERROR_NORMAL;
    szLoadOrderGroup = NULL;
    lpdwTagID	     = NULL;
    szDependencies   = NULL;

    my_argc	     = 0;
    my_argv	     = NULL;
    hShutdownEvent   = 0;
    nError	     = 0;
    dwState	     = 0;
}

/* ------------------------------------------------------------------------

 -------------------------------------------------------------------------- */
NTService::~NTService()
{
  if(ServiceName != NULL) delete[] ServiceName;
}
/* ------------------------------------------------------------------------

 -------------------------------------------------------------------------- */
BOOL NTService::GetOS()
{
  bOsNT = FALSE;
  memset(&osVer, 0, sizeof(OSVERSIONINFO));
  osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if (GetVersionEx(&osVer))
  {
    if (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT)
      bOsNT = TRUE;
  }
  return bOsNT;
}

/* ------------------------------------------------------------------------
 Init()  Registers the main service thread with the service manager

    ServiceThread - pointer to the main programs entry function
		    when the service is started
 -------------------------------------------------------------------------- */
long NTService::Init(LPCSTR szInternName,void *ServiceThread)
{

  pService = this;

  fpServiceThread = (THREAD_FC)ServiceThread;
  ServiceName = new char[lstrlen(szInternName)+1];
  lstrcpy(ServiceName,szInternName);

  SERVICE_TABLE_ENTRY stb[] =
  {
    { (char *)szInternName,(LPSERVICE_MAIN_FUNCTION) ServiceMain} ,
    { NULL, NULL }
  };

  return StartServiceCtrlDispatcher(stb); //register with the Service Manager
}
/* ------------------------------------------------------------------------
  Install() - Installs the service with Service manager
  nError values:
	0  success
	1  Can't open the Service manager
	2  Failed to create service
 -------------------------------------------------------------------------- */
BOOL NTService::Install(LPCSTR szInternName,LPCSTR szDisplayName,
		       LPCSTR szFullPath, LPCSTR szAccountName,LPCSTR szPassword)
{
  SC_HANDLE newService, scm;

  nError=0;

  // open a connection to the SCM
  scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE);
  if(scm)  // Install the new service
  {  newService = CreateService(
	scm,
	szInternName,
	szDisplayName,
	dwDesiredAccess,	//default: SERVICE_ALL_ACCESS
	dwServiceType,		//default: SERVICE_WIN32_OWN_PROCESS
	dwStartType,		//default: SERVICE_AUTOSTART
	dwErrorControl,		//default: SERVICE_ERROR_NORMAL
	szFullPath,		//exec full path
	szLoadOrderGroup,	//default: NULL
	lpdwTagID,		//default: NULL
	szDependencies,		//default: NULL
	szAccountName,		//default: NULL
	szPassword);		//default: NULL

     if (newService) CloseServiceHandle(newService);  // clean up
     else	     nError=2;

     // clean up
     CloseServiceHandle(scm);
  }
  else nError=1;

  return (!nError);
}
/* ------------------------------------------------------------------------
  Remove() - Removes  the service
  nError values:
	0  success
	1  Can't open the Service manager
	2  Failed to locate service
	3  Failed to delete service
 -------------------------------------------------------------------------- */
BOOL NTService::Remove(LPCSTR szInternName)
{

  SC_HANDLE service, scm;

  nError=0;

  // open a connection to the SCM
  scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE);

  if (scm)
  {
    //open the service
    service = OpenService(scm,szInternName, DELETE );
    if(service)
    {
      if(!DeleteService(service)) nError=3;
      CloseServiceHandle(service);
    }
    else
    {
     //MessageBox(NULL,"Can't find the service","Remove Error",MB_OK|MB_ICONHAND);
       nError=2;
    }
    CloseServiceHandle(scm);
  }
  else nError=1;

  return (!nError);
}

/* ------------------------------------------------------------------------
   Stop() - this function should be called before the app. exits to stop
	    the service
 -------------------------------------------------------------------------- */
void NTService::Stop(void)
{
  SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1, 60000);
  StopService();
  SetStatus(SERVICE_STOPPED, NO_ERROR, 0, 1, 1000);
}

/* ------------------------------------------------------------------------
  ServiceMain() - This is the function that is called from the
		  service manager to start the service
 -------------------------------------------------------------------------- */
void NTService::ServiceMain(DWORD argc, LPTSTR *argv)
{

  // registration function
  pService->hServiceStatusHandle =
	   RegisterServiceCtrlHandler(pService->ServiceName,
		       (LPHANDLER_FUNCTION )NTService::ServiceCtrlHandler);

  if(!pService->hServiceStatusHandle)
  {
      pService->Exit(GetLastError());
      return;
  }

  // notify SCM of progress
  if(!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 1, 8000))
  {
      pService->Exit(GetLastError());
      return;
  }

  // create the exit event
  pService->hExitEvent = CreateEvent (0, TRUE, FALSE,0);
  if(!pService->hExitEvent)
  {
      pService->Exit(GetLastError());
      return;
  }

  if(!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 3, pService->nStartTimeOut))
  {
      pService->Exit(GetLastError());
      return;
  }

  // save start arguments
  pService->my_argc=argc;
  pService->my_argv=argv;

  // start the service
  if(!pService->StartService())
  {
      pService->Exit(GetLastError());
      return;
  }

  // the service is now running.
  if(!pService->SetStatus(SERVICE_RUNNING,NO_ERROR, 0, 0, 0))
  {
      pService->Exit(GetLastError());
      return;
  }

  // wait for exit event
  WaitForSingleObject (pService->hExitEvent, INFINITE);

  // wait for thread to exit
  WaitForSingleObject (pService->hThreadHandle, 30000);

  pService->Exit(0);
}

/* ------------------------------------------------------------------------
   StartService() - starts the appliaction thread
 -------------------------------------------------------------------------- */
BOOL NTService::StartService()
{

  // Start the real service's thread (application)
  hThreadHandle = (HANDLE) _beginthread((THREAD_FC)fpServiceThread,0,(void *)this);

  if (hThreadHandle==0) return FALSE;

  bRunning = TRUE;
  return TRUE;
}
/* ------------------------------------------------------------------------

 -------------------------------------------------------------------------- */
void NTService::StopService()
{
  bRunning=FALSE;

  // Set the event for application
  if(hShutdownEvent)
     SetEvent(hShutdownEvent);

  // Set the event for ServiceMain
  SetEvent(hExitEvent);
}
/* ------------------------------------------------------------------------

 -------------------------------------------------------------------------- */
void NTService::PauseService()
{
    bPause = TRUE;
    SuspendThread(hThreadHandle);
}
/* ------------------------------------------------------------------------

 -------------------------------------------------------------------------- */
void NTService::ResumeService()
{
    bPause=FALSE;
    ResumeThread(hThreadHandle);
}
/* ------------------------------------------------------------------------

 -------------------------------------------------------------------------- */
BOOL NTService::SetStatus (DWORD dwCurrentState,DWORD dwWin32ExitCode,
	    DWORD dwServiceSpecificExitCode,DWORD dwCheckPoint,DWORD dwWaitHint)
{
  BOOL bRet;
  SERVICE_STATUS serviceStatus;

   dwState=dwCurrentState;

   serviceStatus.dwServiceType	= SERVICE_WIN32_OWN_PROCESS;
   serviceStatus.dwCurrentState = dwCurrentState;

   if (dwCurrentState == SERVICE_START_PENDING)
	serviceStatus.dwControlsAccepted = 0;	//don't accept conrol events
   else
	serviceStatus.dwControlsAccepted =    SERVICE_ACCEPT_STOP |
	    SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;

   // if a specific exit code is defined,set up the win32 exit code properly
   if (dwServiceSpecificExitCode == 0)
       serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
   else
       serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;

   serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;

   serviceStatus.dwCheckPoint = dwCheckPoint;
   serviceStatus.dwWaitHint   = dwWaitHint;

   // Pass the status to the Service Manager
   bRet=SetServiceStatus (hServiceStatusHandle, &serviceStatus);

   if(!bRet) StopService();

   return bRet;
}
/* ------------------------------------------------------------------------

 -------------------------------------------------------------------------- */
void NTService::ServiceCtrlHandler(DWORD ctrlCode)
{

  DWORD  dwState = 0;

  if(!pService) return;

  dwState=pService->dwState;  // get current state

  switch(ctrlCode)
  {

   /*********** do we need this ? *******************************
    case SERVICE_CONTROL_PAUSE:
	 if (pService->bRunning && ! pService->bPause)
	 {
	     dwState = SERVICE_PAUSED;
	     pService->SetStatus(SERVICE_PAUSE_PENDING,NO_ERROR, 0, 1, pService->nPauseTimeOut);
	     pService->PauseService();
	 }
	 break;

   case SERVICE_CONTROL_CONTINUE:
	if (pService->bRunning && pService->bPause)
	{
	    dwState = SERVICE_RUNNING;
	    pService->SetStatus(SERVICE_CONTINUE_PENDING,NO_ERROR, 0, 1, pService->nResumeTimeOut);
	    pService->ResumeService();
	}
	break;
   ****************************************************************/

    case SERVICE_CONTROL_SHUTDOWN:
    case SERVICE_CONTROL_STOP:
	 dwState = SERVICE_STOP_PENDING;
	 pService->SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1, pService->nStopTimeOut);
	 pService->StopService();
	 break;

   default:
	pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
	break;
  }
  //pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
}
/* ------------------------------------------------------------------------

 -------------------------------------------------------------------------- */
void NTService::Exit(DWORD error)
{
  if (hExitEvent) CloseHandle(hExitEvent);

  // Send a message to the scm to tell that we stop
  if (hServiceStatusHandle)
      SetStatus(SERVICE_STOPPED, error,0, 0, 0);

  // If the thread has started kill it ???
  // if (hThreadHandle) CloseHandle(hThreadHandle);

}

/* ------------------------- the end -------------------------------------- */