charon-svc: Register for stop events not before reaching STATUS_RUNNING

MSDN SetServiceStatus(): "Do not register to accept controls while the status
is SERVICE_START_PENDING or the service can crash."
This commit is contained in:
Martin Willi 2014-05-28 16:14:04 +02:00
parent ef7bfadabb
commit 4ce8b0cae6
1 changed files with 92 additions and 60 deletions

View File

@ -45,6 +45,12 @@ static HANDLE event;
*/ */
extern void (*dbg) (debug_t group, level_t level, char *fmt, ...); extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
/**
* Forward declaration
*/
static DWORD service_handler(DWORD dwControl, DWORD dwEventType,
LPVOID lpEventData, LPVOID lpContext);
/** /**
* Logging hook for library logs, using stderr output * Logging hook for library logs, using stderr output
*/ */
@ -103,9 +109,90 @@ static void update_status(DWORD state)
} }
/** /**
* Initialize and run charon * Control handler for console
*/ */
static void init_and_run(DWORD dwArgc, LPTSTR *lpszArgv) static BOOL console_handler(DWORD dwCtrlType)
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
DBG1(DBG_DMN, "application is stopping, cleaning up");
if (status.dwCurrentState == SERVICE_RUNNING)
{
charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL,
dwCtrlType);
}
/* signal main thread to clean up */
SetEvent(event);
return TRUE;
default:
return FALSE;
}
}
/**
* Service handler function
*/
static DWORD service_handler(DWORD dwControl, DWORD dwEventType,
LPVOID lpEventData, LPVOID lpContext)
{
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
DBG1(DBG_DMN, "service is stopping, cleaning up");
if (status.dwCurrentState == SERVICE_RUNNING)
{
charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL,
dwControl);
}
/* signal main thread to clean up */
SetEvent(event);
return NO_ERROR;
case SERVICE_CONTROL_INTERROGATE:
return NO_ERROR;
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
}
/**
* Wait for console program shutdown
*/
static int console_wait()
{
update_status(SERVICE_RUNNING);
if (WaitForSingleObjectEx(event, INFINITE, TRUE) != WAIT_OBJECT_0)
{
return 2;
}
return 0;
}
/**
* Wait for service shutdown
*/
static int service_wait()
{
/* service is initialized, we now accept control requests */
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
update_status(SERVICE_RUNNING);
status.dwControlsAccepted = 0;
if (WaitForSingleObjectEx(event, INFINITE, TRUE) != WAIT_OBJECT_0)
{
return 2;
}
return 0;
}
/**
* Initialize and run charon using a wait function
*/
static void init_and_run(DWORD dwArgc, LPTSTR *lpszArgv, int (*wait)())
{ {
level_t levels[DBG_MAX]; level_t levels[DBG_MAX];
int i; int i;
@ -138,11 +225,7 @@ static void init_and_run(DWORD dwArgc, LPTSTR *lpszArgv)
charon->start(charon); charon->start(charon);
status.dwWin32ExitCode = 0; status.dwWin32ExitCode = wait();
update_status(SERVICE_RUNNING);
/* main thread goes to sleep */
WaitForSingleObjectEx(event, INFINITE, TRUE);
} }
update_status(SERVICE_STOP_PENDING); update_status(SERVICE_STOP_PENDING);
libcharon_deinit(); libcharon_deinit();
@ -159,30 +242,6 @@ static void init_and_run(DWORD dwArgc, LPTSTR *lpszArgv)
update_status(SERVICE_STOPPED); update_status(SERVICE_STOPPED);
} }
/**
* Control handler for console
*/
static BOOL console_handler(DWORD dwCtrlType)
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
DBG1(DBG_DMN, "application is stopping, cleaning up");
if (status.dwCurrentState == SERVICE_RUNNING)
{
charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL,
dwCtrlType);
}
/* signal main thread to clean up */
SetEvent(event);
return TRUE;
default:
return FALSE;
}
}
/** /**
* Main routine when running from console * Main routine when running from console
*/ */
@ -192,37 +251,11 @@ static void console_main(DWORD dwArgc, LPTSTR *lpszArgv)
if (SetConsoleCtrlHandler(console_handler, TRUE)) if (SetConsoleCtrlHandler(console_handler, TRUE))
{ {
init_and_run(dwArgc, lpszArgv); init_and_run(dwArgc, lpszArgv, console_wait);
SetConsoleCtrlHandler(console_handler, FALSE); SetConsoleCtrlHandler(console_handler, FALSE);
} }
} }
/**
* Service handler function
*/
static DWORD service_handler(DWORD dwControl, DWORD dwEventType,
LPVOID lpEventData, LPVOID lpContext)
{
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
DBG1(DBG_DMN, "service is stopping, cleaning up");
if (status.dwCurrentState == SERVICE_RUNNING)
{
charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL,
dwControl);
}
/* signal main thread to clean up */
SetEvent(event);
return NO_ERROR;
case SERVICE_CONTROL_INTERROGATE:
return NO_ERROR;
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
}
/** /**
* Switch the working directory to the executable directory * Switch the working directory to the executable directory
*/ */
@ -256,7 +289,6 @@ static void service_main(DWORD dwArgc, LPTSTR *lpszArgv)
{ {
memset(&status, 0, sizeof(status)); memset(&status, 0, sizeof(status));
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = 1; status.dwWin32ExitCode = 1;
handle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, service_handler, NULL); handle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, service_handler, NULL);
@ -264,7 +296,7 @@ static void service_main(DWORD dwArgc, LPTSTR *lpszArgv)
{ {
if (switch_workingdir()) if (switch_workingdir())
{ {
init_and_run(dwArgc, lpszArgv); init_and_run(dwArgc, lpszArgv, service_wait);
} }
} }
} }