/****************************************************************************

   MySqlWatch    - WinNT service program MySQL

                 - Re-start MySql server in case of failure

*****************************************************************************/

#include "StdAfx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <tchar.h>


// name of the executable
#define SZAPPNAME            "service"
// internal name of the service
#define SZSERVICENAME        "MySql"
#define SZSERVICEDISPLAYNAME  "MySql"

// displayed name of the service
#define SZDEPENDENCIES       ""


VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);
VOID ServiceStop(void);
BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
void AddToMessageLog(LPTSTR lpszMsg);

void CmdRestartService(char *szServiceName);
void StopService(char *szServiceName);

// internal variables

SERVICE_STATUS          ssStatus;       // current status of the service
SERVICE_STATUS_HANDLE   sshStatusHandle;
DWORD                   dwErr = 0;
BOOL                    bDebug = FALSE;
TCHAR                   szErr[256];

// internal function prototypes
void WINAPI service_ctrl(DWORD dwCtrlCode);
void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
void CmdInstallService(void);
void CmdRemoveService(void);
void CmdDebugService(int argc, char **argv);
BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );

//
//  FUNCTION: main
//
//  PURPOSE: entrypoint for service
//
//  PARAMETERS:
//    argc - number of command line arguments
//    argv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    main() either performs the command line task, or
//    call StartServiceCtrlDispatcher to register the
//    main service thread.  When the this call returns,
//    the service has stopped, so exit.
//
void main(int argc, char **argv)
{
    SERVICE_TABLE_ENTRY dispatchTable[] =
    {
        { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main },
        { NULL, NULL }
    };

    if ( (argc > 1) &&
         ((*argv[1] == '-') || (*argv[1] == '/')) )
    {
        if ( stricmp( "start", argv[1]+1 ) == 0 )
        {
          CmdRestartService("MySql");
        }
        else if ( stricmp( "stop", argv[1]+1 ) == 0 )
        {
          StopService("MySql");
        }
        else
            goto dispatch;
        exit(0);
    }

    // if it doesn't match any of the above parameters
    // the service control manager may be starting the service
    // so we must call StartServiceCtrlDispatcher
    dispatch:
        // this is just to be friendly
        printf( "%s -start            to remove the service\n", SZAPPNAME );
        printf( "%s -stop           to remove the service\n", SZAPPNAME );
}

//
//  FUNCTION: service_main
//
//  PURPOSE: To perform actual initialization of the service
//
//  PARAMETERS:
//    dwArgc   - number of command line arguments
//    lpszArgv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    This routine performs the service initialization and then calls
//    the user defined ServiceStart() routine to perform majority
//    of the work.
//


void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
{
    // register our service control handler:
    //
    sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
    if (!sshStatusHandle)
        goto cleanup;

    // SERVICE_STATUS members that don't change in example
    //
    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ssStatus.dwServiceSpecificExitCode = 0;

    // report the status to the service control manager.
    //

    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;
    ServiceStart( dwArgc, lpszArgv );

cleanup:
    // try to report the stopped status to the service control manager.
    //
    if (sshStatusHandle)
        ReportStatusToSCMgr(
                            SERVICE_STOPPED,
                            dwErr,
                            0);
    return;
}

//
//  FUNCTION: service_ctrl
//
//  PURPOSE: This function is called by the SCM whenever
//           ControlService() is called on this service.
//
//  PARAMETERS:
//    dwCtrlCode - type of control requested
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void WINAPI service_ctrl(DWORD dwCtrlCode)
{
    // Handle the requested control code.
    //
    switch(dwCtrlCode)
    {
        // Stop the service.
        //
        case SERVICE_CONTROL_STOP:
            ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
            ServiceStop();
            break;

        // Update the service status.
       //
        case SERVICE_CONTROL_INTERROGATE:
            break;

        // invalid control code
        //
        default:
            break;
    }

    ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
}

//
//  FUNCTION: ReportStatusToSCMgr()
//
//  PURPOSE: Sets the current status of the service and
//           reports it to the Service Control Manager
//
//  PARAMETERS:
//    dwCurrentState - the state of the service
//    dwWin32ExitCode - error code to report
//    dwWaitHint - worst case estimate to next checkpoint
//
//  RETURN VALUE:
//    TRUE  - success
//    FALSE - failure
//
//  COMMENTS:
//

BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
                         DWORD dwWin32ExitCode,
                         DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;
    BOOL fResult = TRUE;

    if ( !bDebug ) // when debugging we don't report to the SCM
    {
        if (dwCurrentState == SERVICE_START_PENDING)
            ssStatus.dwControlsAccepted = 0;
        else
            ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

        ssStatus.dwCurrentState = dwCurrentState;
        ssStatus.dwWin32ExitCode = dwWin32ExitCode;
        ssStatus.dwWaitHint = dwWaitHint;

        if ( ( dwCurrentState == SERVICE_RUNNING ) ||
             ( dwCurrentState == SERVICE_STOPPED ) )
            ssStatus.dwCheckPoint = 0;
        else
            ssStatus.dwCheckPoint = dwCheckPoint++;

        // Report the status of the service to the service control manager.
        //
        if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
            AddToMessageLog(TEXT("SetServiceStatus"));
        }
    }
    return fResult;
}

//
//  FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
//
//  PURPOSE: Allows any thread to log an error message
//
//  PARAMETERS:
//    lpszMsg - text for message
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void AddToMessageLog(LPTSTR lpszMsg)
{
  /*
    TCHAR   szMsg[256];
    HANDLE  hEventSource;
    LPTSTR  lpszStrings[2];

    if ( !bDebug )
    {
        dwErr = GetLastError();
        // Use event logging to log the error.
        //
        hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
        _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
        lpszStrings[0] = szMsg;
        lpszStrings[1] = lpszMsg;

        if (hEventSource != NULL) {
            ReportEvent(hEventSource, // handle of event source
                EVENTLOG_ERROR_TYPE,  // event type
                0,                    // event category
                0,                    // event ID
                NULL,                 // current user's SID
                2,                    // strings in lpszStrings
                0,                    // no bytes of raw data
                lpszStrings,          // array of error strings
                NULL);                // no raw data
             DeregisterEventSource(hEventSource);
        }
    }
    */
}

//
//  FUNCTION: CmdRestartService()
//
//  PURPOSE: Stops and removes the service
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void CmdRestartService(char *szServiceName)
{
    SC_HANDLE   schService;
    SC_HANDLE   schSCManager;
    schSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
    if ( schSCManager )
    {
        schService = OpenService(schSCManager, TEXT(szServiceName), SERVICE_ALL_ACCESS);
        if (schService)
        {
            if(! ControlService( schService, SERVICE_CONTROL_INTERROGATE, &ssStatus ) )
            //if(QueryServiceStatus( schService, &ssStatus )==0)
            {
                if(GetLastError()==ERROR_SERVICE_NOT_ACTIVE)
                {
                   //AddToMessageLog(TEXT("Start service..."));
                   StartService( schService, 0,NULL);
                }
                else
                {  ;
                   //AddToMessageLog(TEXT("QueryService..."));
                   //AddToMessageLog(TEXT(GetLastErrorText(szErr,256)));
                }
            }
            CloseServiceHandle(schService);
        }
        else
        {    _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
              AddToMessageLog(TEXT("OpenService..."));
              AddToMessageLog(TEXT(GetLastErrorText(szErr,256)));
        }
        CloseServiceHandle(schSCManager);
    }
    else
    {    _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
          AddToMessageLog(TEXT("OpenSCMManager.."));
    }
}


void StopService(char *szServiceName)
{
   SC_HANDLE   schService;
   SC_HANDLE   schSCManager;
   schSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
    if ( schSCManager )
    {
        schService = OpenService(schSCManager, TEXT(szServiceName), SERVICE_ALL_ACCESS);
        if (schService)
        {
                    if (! ControlService(schService, SERVICE_CONTROL_STOP, &ssStatus))
                    printf ("Error stoping service...");

                   //AddToMessageLog(TEXT("QueryService..."));
                   //AddToMessageLog(TEXT(GetLastErrorText(szErr,256)));
            CloseServiceHandle(schService);
        }
        else
        {    _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
              AddToMessageLog(TEXT("OpenService..."));
              AddToMessageLog(TEXT(GetLastErrorText(szErr,256)));
        }
        CloseServiceHandle(schSCManager);
    }
    else
    {    _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
          AddToMessageLog(TEXT("OpenSCMManager.."));
    }
}

///////////////////////////////////////////////////////////////////
//
//  The following code is for running the service as a console app
//
//
//  FUNCTION: CmdDebugService(int argc, char ** argv)
//
//  PURPOSE: Runs the service as a console application
//
//  PARAMETERS:
//    argc - number of command line arguments
//    argv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void CmdDebugService(int argc, char ** argv)
{
    DWORD dwArgc;
    LPTSTR *lpszArgv;

#ifdef UNICODE
    lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
#else
    dwArgc   = (DWORD) argc;
    lpszArgv = argv;
#endif
    _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
    SetConsoleCtrlHandler( ControlHandler, TRUE );
    ServiceStart( dwArgc, lpszArgv );
}

//
//  FUNCTION: ControlHandler ( DWORD dwCtrlType )
//
//  PURPOSE: Handled console control events
//
//  PARAMETERS:
//    dwCtrlType - type of control event
//
//  RETURN VALUE:
//    True - handled
//    False - unhandled
//
//  COMMENTS:
//
BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
{
    switch( dwCtrlType )
    {
        case CTRL_BREAK_EVENT:  // use Ctrl+C or Ctrl+Break to simulate
        case CTRL_C_EVENT:      // SERVICE_CONTROL_STOP in debug mode
            _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
            ServiceStop();
            return TRUE;
            break;
    }
    return FALSE;
}

//
//  FUNCTION: GetLastErrorText
//
//  PURPOSE: copies error message text to string
//
//  PARAMETERS:
//    lpszBuf - destination buffer
//    dwSize - size of buffer
//
//  RETURN VALUE:
//    destination buffer
//
//  COMMENTS:
//
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
{
    DWORD dwRet;
    LPTSTR lpszTemp = NULL;
    dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
                           NULL,
                           GetLastError(),
                           LANG_NEUTRAL,
                           (LPTSTR)&lpszTemp,
                           0,
                           NULL );

    // supplied buffer is not long enough

    if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
        lpszBuf[0] = TEXT('\0');
    else
    {
        lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0');  //remove cr and newline character
        _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
    }
    if ( lpszTemp )
        LocalFree((HLOCAL) lpszTemp );
    return lpszBuf;
}

//-------------------------------------------------
// this event is signalled when the
// service should end
//-------------------------------------------------

HANDLE  hServerStopEvent = NULL;

//-------------------------------------------------
//  FUNCTION: ServiceStart
//
//  PURPOSE: Actual code of the service
//           that does the work.
//-------------------------------------------------
void ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)
{
    DWORD  dwWait,dwTimeout=1000*60*1;

    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;

    // create the event object. The control handler function signals
    // this event when it receives the "stop" control code.
    //
    hServerStopEvent = CreateEvent(
        NULL,    // no security attributes
        TRUE,    // manual reset event
        FALSE,   // not-signalled
        NULL);   // no name

    if ( hServerStopEvent == NULL) goto cleanup;

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_RUNNING,       // service state
        NO_ERROR,              // exit code
        0))                    // wait hint
        goto cleanup;

    //
    // End of initialization
    // Service is now running, perform work until shutdown
    //
    while ( 1 )
    {
        dwWait = WaitForSingleObject(  hServerStopEvent, dwTimeout);
        if(dwWait==WAIT_FAILED)
        {
           AddToMessageLog(TEXT("Error in WaitForSingleObject"));
           break;
        }
        else if(dwWait==WAIT_TIMEOUT)
        {
            CmdRestartService("MySql");
        }
        else
          break;  //shutdown
    }
  cleanup:
    if (hServerStopEvent)
        CloseHandle(hServerStopEvent);
}

//-------------------------------------------------
//  FUNCTION: ServiceStop
//
//  PURPOSE: Stops the service
//-------------------------------------------------

void ServiceStop()
{
    if ( hServerStopEvent )
        SetEvent(hServerStopEvent);
}
//-the end ----------------------------------------





========================================================================
       CONSOLE APPLICATION : service
========================================================================


AppWizard has created this service application for you.  

This file contains a summary of what you will find in each of the files that
make up your service application.

service.dsp
    This file (the project file) contains information at the project level and
    is used to build a single project or subproject. Other users can share the
    project (.dsp) file, but they should export the makefiles locally.

service.cpp
    This is the main application source file.


/////////////////////////////////////////////////////////////////////////////
Other standard files:

StdAfx.h, StdAfx.cpp
    These files are used to build a precompiled header (PCH) file
    named service.pch and a precompiled types file named StdAfx.obj.


/////////////////////////////////////////////////////////////////////////////
Other notes:

AppWizard uses "TODO:" to indicate parts of the source code you
should add to or customize.

/////////////////////////////////////////////////////////////////////////////
