Correct behavior with passing back errors on onStart, onStop.
This commit is contained in:
@@ -15,6 +15,9 @@ type Service interface {
|
|||||||
// Call quickly after initial entry point. Does not return until
|
// Call quickly after initial entry point. Does not return until
|
||||||
// service is ready to stop. onStart is called when the service is
|
// service is ready to stop. onStart is called when the service is
|
||||||
// starting, returning an error will fail to start the service.
|
// starting, returning an error will fail to start the service.
|
||||||
|
// If an error is returned from onStop, the service will still stop.
|
||||||
|
// An error passed from onStart or onStop will be returned as
|
||||||
|
// an error from Run.
|
||||||
// Both callbacks should return quickly and not block.
|
// Both callbacks should return quickly and not block.
|
||||||
Run(onStart, onStop func() error) error
|
Run(onStart, onStop func() error) error
|
||||||
|
|
||||||
|
|||||||
+163
-161
@@ -1,190 +1,192 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
SERVICE_STATUS gSvcStatus;
|
SERVICE_STATUS gSvcStatus;
|
||||||
SERVICE_STATUS_HANDLE gSvcStatusHandle;
|
SERVICE_STATUS_HANDLE gSvcStatusHandle;
|
||||||
HANDLE ghSvcStopEvent = NULL;
|
HANDLE ghSvcStopEvent = NULL;
|
||||||
|
|
||||||
void SvcInstall(void);
|
void SvcInstall(void);
|
||||||
void WINAPI SvcCtrlHandler( DWORD );
|
void WINAPI SvcCtrlHandler( DWORD );
|
||||||
void WINAPI SvcMain( DWORD, LPTSTR * );
|
void WINAPI SvcMain( DWORD, LPTSTR * );
|
||||||
|
|
||||||
void ReportSvcStatus( DWORD, DWORD, DWORD );
|
void ReportSvcStatus( DWORD, DWORD, DWORD );
|
||||||
|
|
||||||
static char *goServiceName;
|
static char *goServiceName;
|
||||||
|
|
||||||
int goSigStart = 0;
|
int goSigStart = 0;
|
||||||
int goAckStart = 0;
|
int goAckStart = 0;
|
||||||
HANDLE goWaitStart = NULL;
|
HANDLE goWaitStart = NULL;
|
||||||
|
|
||||||
int goSigStop = 0;
|
int goSigStop = 0;
|
||||||
int goAckStop = 0;
|
int goAckStop = 0;
|
||||||
HANDLE goWaitStop = NULL;
|
HANDLE goWaitStop = NULL;
|
||||||
|
|
||||||
int goSigError = 0;
|
int goSigError = 0;
|
||||||
char *errorText;
|
char *errorText;
|
||||||
|
|
||||||
void
|
void
|
||||||
continueStart(int response) {
|
continueStart(int response) {
|
||||||
goSigStart = 0;
|
goSigStart = 0;
|
||||||
goAckStart = response;
|
goAckStart = response;
|
||||||
SetEvent(goWaitStart);
|
SetEvent(goWaitStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
continueStop(int response) {
|
continueStop(int response) {
|
||||||
goSigStop = 0;
|
goSigStop = 0;
|
||||||
goAckStop = response;
|
goAckStop = response;
|
||||||
SetEvent(goWaitStop);
|
SetEvent(goWaitStop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
signalError(char *text) {
|
signalError(char *text) {
|
||||||
errorText = text;
|
errorText = text;
|
||||||
goSigError = 1;
|
goSigError = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
initService(char *serviceName) {
|
initService(char *serviceName) {
|
||||||
if(!goServiceName) {
|
if(!goServiceName) {
|
||||||
free(goServiceName);
|
free(goServiceName);
|
||||||
}
|
}
|
||||||
goServiceName = serviceName;
|
goServiceName = serviceName;
|
||||||
SERVICE_TABLE_ENTRY DispatchTable[] = {
|
SERVICE_TABLE_ENTRY DispatchTable[] = {
|
||||||
{ serviceName, (LPSERVICE_MAIN_FUNCTION) SvcMain },
|
{ serviceName, (LPSERVICE_MAIN_FUNCTION) SvcMain },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
// This call returns when the service has stopped.
|
// This call returns when the service has stopped.
|
||||||
// The process should simply terminate when the call returns.
|
// The process should simply terminate when the call returns.
|
||||||
if (!StartServiceCtrlDispatcher( DispatchTable )) {
|
if (!StartServiceCtrlDispatcher( DispatchTable )) {
|
||||||
signalError("StartServiceCtrlDispatcher");
|
signalError("StartServiceCtrlDispatcher");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entry point for the service
|
// Entry point for the service
|
||||||
// dwArgc - Number of arguments in the lpszArgv array
|
// dwArgc - Number of arguments in the lpszArgv array
|
||||||
// lpszArgv - Array of strings. The first string is the name of
|
// lpszArgv - Array of strings. The first string is the name of
|
||||||
// the service and subsequent strings are passed by the process
|
// the service and subsequent strings are passed by the process
|
||||||
// that called the StartService function to start the service.
|
// that called the StartService function to start the service.
|
||||||
VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
|
VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
|
||||||
{
|
{
|
||||||
// Register the handler function for the service
|
// Register the handler function for the service
|
||||||
|
|
||||||
gSvcStatusHandle = RegisterServiceCtrlHandler(
|
gSvcStatusHandle = RegisterServiceCtrlHandler(
|
||||||
goServiceName,
|
goServiceName,
|
||||||
SvcCtrlHandler);
|
SvcCtrlHandler);
|
||||||
|
|
||||||
if( !gSvcStatusHandle ) {
|
if( !gSvcStatusHandle ) {
|
||||||
signalError("RegisterServiceCtrlHandler");
|
signalError("RegisterServiceCtrlHandler");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These SERVICE_STATUS members remain as set here
|
// These SERVICE_STATUS members remain as set here
|
||||||
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||||
gSvcStatus.dwServiceSpecificExitCode = 0;
|
gSvcStatus.dwServiceSpecificExitCode = 0;
|
||||||
|
|
||||||
// Report initial status to the SCM
|
// Report initial status to the SCM
|
||||||
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );
|
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );
|
||||||
|
|
||||||
goWaitStart = CreateEvent(NULL, TRUE, FALSE, NULL);
|
goWaitStart = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
goWaitStop = CreateEvent(NULL, TRUE, FALSE, NULL);
|
goWaitStop = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
|
||||||
// signal go to start
|
// signal go to start
|
||||||
// wait for go to confirm
|
// wait for go to confirm
|
||||||
goSigStart = 1;
|
goSigStart = 1;
|
||||||
WaitForSingleObject(goWaitStart, INFINITE);
|
WaitForSingleObject(goWaitStart, INFINITE);
|
||||||
if(goAckStart != 1) {
|
if(goAckStart != 1) {
|
||||||
return;
|
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Declare and set any required variables.
|
// TODO: Declare and set any required variables.
|
||||||
// Be sure to periodically call ReportSvcStatus() with
|
// Be sure to periodically call ReportSvcStatus() with
|
||||||
// SERVICE_START_PENDING. If initialization fails, call
|
// SERVICE_START_PENDING. If initialization fails, call
|
||||||
// ReportSvcStatus with SERVICE_STOPPED.
|
// ReportSvcStatus with SERVICE_STOPPED.
|
||||||
|
|
||||||
// Create an event. The control handler function, SvcCtrlHandler,
|
// Create an event. The control handler function, SvcCtrlHandler,
|
||||||
// signals this event when it receives the stop control code.
|
// signals this event when it receives the stop control code.
|
||||||
ghSvcStopEvent = CreateEvent(
|
ghSvcStopEvent = CreateEvent(
|
||||||
NULL, // default security attributes
|
NULL, // default security attributes
|
||||||
TRUE, // manual reset event
|
TRUE, // manual reset event
|
||||||
FALSE, // not signaled
|
FALSE, // not signaled
|
||||||
NULL); // no name
|
NULL); // no name
|
||||||
|
|
||||||
if ( ghSvcStopEvent == NULL)
|
if ( ghSvcStopEvent == NULL)
|
||||||
{
|
{
|
||||||
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
|
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report running status when initialization is complete.
|
// Report running status when initialization is complete.
|
||||||
ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );
|
ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
|
|
||||||
WaitForSingleObject(ghSvcStopEvent, INFINITE);
|
WaitForSingleObject(ghSvcStopEvent, INFINITE);
|
||||||
|
|
||||||
// Signal service Stopped
|
// Signal service Stopped
|
||||||
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
|
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the current service status and reports it to the SCM.
|
// Sets the current service status and reports it to the SCM.
|
||||||
// dwCurrentState - The current state (see SERVICE_STATUS)
|
// dwCurrentState - The current state (see SERVICE_STATUS)
|
||||||
// dwWin32ExitCode - The system error code
|
// dwWin32ExitCode - The system error code
|
||||||
// dwWaitHint - Estimated time for pending operation, in milliseconds
|
// dwWaitHint - Estimated time for pending operation, in milliseconds
|
||||||
VOID ReportSvcStatus( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) {
|
VOID ReportSvcStatus( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) {
|
||||||
static DWORD dwCheckPoint = 1;
|
static DWORD dwCheckPoint = 1;
|
||||||
|
|
||||||
// Fill in the SERVICE_STATUS structure.
|
// Fill in the SERVICE_STATUS structure.
|
||||||
gSvcStatus.dwCurrentState = dwCurrentState;
|
gSvcStatus.dwCurrentState = dwCurrentState;
|
||||||
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
|
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
|
||||||
gSvcStatus.dwWaitHint = dwWaitHint;
|
gSvcStatus.dwWaitHint = dwWaitHint;
|
||||||
|
|
||||||
if (dwCurrentState == SERVICE_START_PENDING) {
|
if (dwCurrentState == SERVICE_START_PENDING) {
|
||||||
gSvcStatus.dwControlsAccepted = 0;
|
gSvcStatus.dwControlsAccepted = 0;
|
||||||
} else {
|
} else {
|
||||||
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) ) {
|
if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) ) {
|
||||||
gSvcStatus.dwCheckPoint = 0;
|
gSvcStatus.dwCheckPoint = 0;
|
||||||
} else {
|
} else {
|
||||||
gSvcStatus.dwCheckPoint = dwCheckPoint++;
|
gSvcStatus.dwCheckPoint = dwCheckPoint++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report the status of the service to the SCM.
|
// Report the status of the service to the SCM.
|
||||||
SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
|
SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by SCM whenever a control code is sent to the service
|
// Called by SCM whenever a control code is sent to the service
|
||||||
// using the ControlService function.
|
// using the ControlService function.
|
||||||
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl ) {
|
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl ) {
|
||||||
// Handle the requested control code.
|
// Handle the requested control code.
|
||||||
switch(dwCtrl) {
|
switch(dwCtrl) {
|
||||||
case SERVICE_CONTROL_STOP:
|
case SERVICE_CONTROL_STOP:
|
||||||
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
|
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
|
||||||
|
|
||||||
goSigStop = 1;
|
goSigStop = 1;
|
||||||
WaitForSingleObject(goWaitStop, INFINITE);
|
WaitForSingleObject(goWaitStop, INFINITE);
|
||||||
if(goAckStop != 1) {
|
if(goAckStop != 1) {
|
||||||
return;
|
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Now signal on the initial thread to stop blocking.
|
// Now signal on the initial thread to stop blocking.
|
||||||
SetEvent(ghSvcStopEvent);
|
SetEvent(ghSvcStopEvent);
|
||||||
return;
|
return;
|
||||||
case SERVICE_CONTROL_INTERROGATE:
|
case SERVICE_CONTROL_INTERROGATE:
|
||||||
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
|
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
@@ -193,26 +195,26 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Starts a windows service routine. Service must be registered first.
|
// Starts a windows service routine. Service must be registered first.
|
||||||
// Call blocks until an error occurs or the service stops. If onStart returns
|
// Call blocks until an error occurs or the service stops. If onStart returns
|
||||||
// an error, service will not start. If onStop returns an error, the service will
|
// an error, service will not start. If onStop returns an error, the service will
|
||||||
// not stop.
|
// not stop.
|
||||||
func runService(serviceName string, onStart, onStop func() error) error {
|
func runService(serviceName string, onStart, onStop func() error) error {
|
||||||
// We alloc a c string here, but do not free it here
|
// We alloc a c string here, but do not free it here
|
||||||
cname := C.CString(serviceName)
|
cname := C.CString(serviceName)
|
||||||
|
|
||||||
retErr := make(chan error, 1)
|
retErr := make(chan error, 1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
// Check C vars on timer.
|
// Check C vars on timer.
|
||||||
ticker := time.NewTicker(time.Second * 1)
|
ticker := time.NewTicker(time.Second * 1)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for _ = range ticker.C {
|
for _ = range ticker.C {
|
||||||
if C.goSigStart == 1 {
|
if C.goSigStart == 1 {
|
||||||
err := onStart()
|
err := onStart()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// An error was returned.
|
// An error was returned.
|
||||||
// Signal to NOT start the service.
|
// Signal to NOT start the service.
|
||||||
C.continueStart(-1)
|
C.continueStart(-1)
|
||||||
retErr <- err
|
retErr <- err
|
||||||
return
|
return
|
||||||
@@ -221,8 +223,8 @@ func runService(serviceName string, onStart, onStop func() error) error {
|
|||||||
} else if C.goSigStop == 1 {
|
} else if C.goSigStop == 1 {
|
||||||
err := onStop()
|
err := onStop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// An error was returned.
|
// An error was returned.
|
||||||
// Signal to NOT stop the service.
|
// Will signal to stop service.
|
||||||
C.continueStop(-1)
|
C.continueStop(-1)
|
||||||
retErr <- err
|
retErr <- err
|
||||||
return
|
return
|
||||||
@@ -230,7 +232,7 @@ func runService(serviceName string, onStart, onStop func() error) error {
|
|||||||
C.continueStop(1)
|
C.continueStop(1)
|
||||||
retErr <- nil
|
retErr <- nil
|
||||||
} else if C.goSigError == 1 {
|
} else if C.goSigError == 1 {
|
||||||
// Check for service errors.
|
// Check for service errors.
|
||||||
errText := "SERVICE ERROR: "
|
errText := "SERVICE ERROR: "
|
||||||
if C.errorText != nil {
|
if C.errorText != nil {
|
||||||
errText += C.GoString(C.errorText)
|
errText += C.GoString(C.errorText)
|
||||||
|
|||||||
Reference in New Issue
Block a user