/***************************************************************************
 *                                                                         *
 *   MAJORSVC.C                                                            *
 *                                                                         *
 *   Copyright (c) 1996-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Functions used by MAJORBBS.C when run as a service                    *
 *                                                                         *
 *                                        - I. Minkin 6/05/96              *
 *                                                                         *
 ***************************************************************************/

#include <windows.h>
#include "gcomm.h"
#include "majorbbs.h"
#include "majorsvc.h"
#include "svcnames.h"

#define REPORTCURRENTSTATE \
             0xFFFFFFF0            // report current state to SCM

struct tagReportInfo {
     HANDLE hEvent;
     GBOOL fStarting;
};

GBOOL fInitComplete=FALSE;         // done initializing

static GBOOL fStopReceived=FALSE;  // received STOP from SCM
static SERVICE_STATUS_HANDLE hServStatus;    // service control handle
static HANDLE hProgressEvent=NULL;
static CRITICAL_SECTION inReportState;

static VOID WINAPI serviceControl(DWORD ctrlCode);
static DWORD WINAPI reportProgressToSCM(VOID *pReportInfo);
static GBOOL isWgstartRunning(VOID);
static VOID saveExitCode(VOID);

VOID WINAPI
serviceControl(                    // hahdle control codes from SCM
DWORD ctrlCode)                    //   control code
{
     switch (ctrlCode) {
//     case SERVICE_CONTROL_SHUTDOWN:
     case SERVICE_CONTROL_STOP:
          fStopReceived=TRUE;
          shutdownMainThread();
          reportStatusToSCM(SERVICE_STOP_PENDING,NO_ERROR,30000);
          break;
     default:
          reportStatusToSCM(REPORTCURRENTSTATE,NO_ERROR,0);
          break;
     }
}

GBOOL                              //   return TRUE-Ok FALSE-failure
reportStatusToSCM(                 // report new status to SCM
ULONG newState,                    //   new status to report
ULONG win32ExitCode,               //   Win32 API exit code
ULONG waitHint)                    //   wait hint
{
     static DWORD checkPoint=1;
     static DWORD currentState=SERVICE_STOPPED;
     GBOOL fResult;
     SERVICE_STATUS ssStatus;

     EnterCriticalSection(&inReportState);
     if (newState != REPORTCURRENTSTATE) {
          currentState=newState;
     }
     ssStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS;
     if (win32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR) {
          ssStatus.dwServiceSpecificExitCode=errcod;
     }
     else {
          ssStatus.dwServiceSpecificExitCode=0;
     }
     ssStatus.dwCurrentState=currentState;
     ssStatus.dwWin32ExitCode=win32ExitCode;
     ssStatus.dwWaitHint=waitHint;
     if (currentState == SERVICE_START_PENDING
      || currentState == SERVICE_STOP_PENDING) {
          ssStatus.dwControlsAccepted=0;
     }
     else {
          ssStatus.dwControlsAccepted=SERVICE_ACCEPT_STOP;
     }
     if (currentState == SERVICE_RUNNING
      || currentState == SERVICE_STOPPED) {
          ssStatus.dwCheckPoint=0;
     }
     else {
          ssStatus.dwCheckPoint=checkPoint++;
     }
     if (!(fResult=SetServiceStatus(hServStatus,&ssStatus))) {
          elogAPIError("SetServiceStatus Failed");
     }
     LeaveCriticalSection(&inReportState);
     return(fResult);
}

GBOOL                              //   return TRUE-Ok FALSE-failure
serviceInit(                       // prepare to start as a service
HANDLE *hReportTh)                 //   handle to status report thread
{
     InitializeCriticalSection(&inReportState);
     hServStatus=RegisterServiceCtrlHandler(SMAINNAME,serviceControl);
     if (hServStatus == NULL) {
          elogAPIError("RegisterServiceCtrlHandler failed");
          return(FALSE);
     }
     if (!reportStatusToSCM(SERVICE_START_PENDING,NO_ERROR,30000)) {
          elogAPIError("reportStatusToSCM failed");
          return(FALSE);
     }
     hProgressEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
     *hReportTh=runReportTh(TRUE);
     if (!isWgstartRunning()) {
          MessageBox(NULL,"Please use \"" SVR_FULL "\" to initiate the service",
                     SMAINDISPNAME,MB_DEFAULT_DESKTOP_ONLY|MB_ICONSTOP|MB_OK);
          return(FALSE);
     }
     return(TRUE);
}

VOID
serviceShutdown(                   // shuts down service
HANDLE hReportTh)                  //   report thread handle (or NULL)
{
     const CHAR *pInsertStg[1];

     saveExitCode();
     terminateReportTh(hReportTh);
     pInsertStg[0]=SMAINDISPNAME;
     if (!fInitComplete) {
          elogStatus(ELOG_SERVICE_FAILEDTOSTART,pInsertStg,1);
     }
     else {
          elogStatus(ELOG_SERVICE_STOPPED,pInsertStg,1);
     }
     if (fStopReceived) {
          errcod=1;                // operator shutdown
     }
     if (!fInitComplete) {
          reportStatusToSCM(SERVICE_STOPPED,ERROR_SERVICE_SPECIFIC_ERROR,0);
     }
     else {
          reportStatusToSCM(SERVICE_STOPPED,NO_ERROR,0);
     }
     // IMPORTANT
     // after SCM receives status SERVICE_STOPPED it terminates the service
     // so nothing below ever gets executed
}

HANDLE                             //   return new thread handle
runReportTh(                       // run status report thread
GBOOL fStarting)                   //   report START_PENDING or STOP_PENDING
{
     HANDLE hTh;
     DWORD thID;
     static struct tagReportInfo rptInfo;

     ResetEvent(hProgressEvent);
     rptInfo.hEvent=hProgressEvent;
     rptInfo.fStarting=fStarting;
     hTh=CreateThread(NULL,0,reportProgressToSCM,&rptInfo,0,&thID);
     SetThreadPriority(hTh,THREAD_PRIORITY_LOWEST);
     return(hTh);
}

VOID
terminateReportTh(                 // terminates status report thread
HANDLE hReportTh)                  //   thread handle
{
     DWORD exitCode;

     if (hReportTh != NULL && GetExitCodeThread(hReportTh,&exitCode)
      && exitCode == STILL_ACTIVE) {
          SetEvent(hProgressEvent);
          SetThreadPriority(hReportTh,THREAD_PRIORITY_HIGHEST);
          WaitForSingleObject(hReportTh,1000);
          CloseHandle(hReportTh);
     }
}

static DWORD WINAPI                //   return exit code
reportProgressToSCM(               // report START/STOP status to SCM
VOID *pReportInfo)                 //   parameters
{
     DWORD dwCntr=0;
     struct tagReportInfo rptInfo=*(struct tagReportInfo *)pReportInfo;

     while (WaitForSingleObject(rptInfo.hEvent,2000) == WAIT_TIMEOUT) {
          if (rptInfo.fStarting) {
               reportStatusToSCM(SERVICE_START_PENDING,NO_ERROR,30000);
          }
          else {
               reportStatusToSCM(SERVICE_STOP_PENDING,NO_ERROR,30000);
          }
          // this code tries to prevent an unlikely event when main thread dies
          // during startup but SCM is unaware of that so if we run for more
          // than 5 min something is wrong
          if (dwCntr++ > 300) {
               reportStatusToSCM(SERVICE_STOPPED,SMAIN_FAILEDTOSTART,0);
               break;
          }
     }
     return(0);
}

static GBOOL                       //   return TRUE if WGSTART is running
isWgstartRunning(VOID)             // check if WGSTART is running
{
     SERVICE_STATUS ssStatus;
     SC_HANDLE hMainServ=NULL;
     SC_HANDLE hSCManager;
     GBOOL retVal=TRUE;

     if ((hSCManager=OpenSCManager(NULL,NULL,GENERIC_READ)) == NULL) {
          elogAPIError("OpenSCManager failed");
          retVal=FALSE;
     }
     if (retVal
      && (hMainServ=OpenService(hSCManager,SSTARTNAME,SERVICE_QUERY_STATUS))
                                                                      == NULL) {
          elogAPIError("OpenService failed");
          retVal=FALSE;
     }
     if (retVal && !QueryServiceStatus(hMainServ,&ssStatus)) {
          elogAPIError("QueryServiceStatus failed");
          retVal=FALSE;
     }
     if (retVal && ssStatus.dwCurrentState != SERVICE_RUNNING) {
          retVal=FALSE;
     }
     if (hMainServ) {
          CloseServiceHandle(hMainServ);
     }
     if (hSCManager) {
          CloseServiceHandle(hSCManager);
     }
     return(retVal);
}

static VOID
saveExitCode(VOID)
{
     WGSEXITCODE *wec;
     HANDLE hFile;

     hFile=OpenFileMapping(FILE_MAP_WRITE,FALSE,EXITCODE_FILENAME);
     if (hFile == NULL) {
          elogAPIError("OpenFileMapping failed.");
          return;
     }
     wec=(WGSEXITCODE *)MapViewOfFile(hFile,FILE_MAP_WRITE,0,0,sizeof(WGSEXITCODE));
     if (wec == NULL) {
          CloseHandle(hFile);
          elogAPIError("MapViewOfFile failed.");
          return;
     }
     wec->timeTag=GetTickCount();
     wec->exitCode=errcod;
     UnmapViewOfFile(wec);
     CloseHandle(hFile);
}
