summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <abaumann@yahoo.com>2009-03-27 18:31:27 +0100
committerAndreas Baumann <abaumann@yahoo.com>2009-03-27 18:31:27 +0100
commitbaa035aa68b7b72fe301c933227aa2530bbec66d (patch)
treed42e0636d34289a4c00645f5cc115e26e0bb91e0
parentf83210c88e7376e12b5cf0e86a073c59e4185efb (diff)
downloadwolfbones-baa035aa68b7b72fe301c933227aa2530bbec66d.tar.gz
wolfbones-baa035aa68b7b72fe301c933227aa2530bbec66d.tar.bz2
added service termination on console control events
-rw-r--r--docs/service/README8
-rw-r--r--include/wolf/log/messages.h2
-rw-r--r--include/wolf/service/service.h8
-rw-r--r--src/service/service.c104
-rw-r--r--tests/service/testservice.c2
5 files changed, 112 insertions, 12 deletions
diff --git a/docs/service/README b/docs/service/README
index c3b6c56..a046212 100644
--- a/docs/service/README
+++ b/docs/service/README
@@ -11,4 +11,10 @@ We should try to get to a set of service functions as for the daemon.
The normal foreground mode in the console should also be possible
and act on events:
-http://www.codeproject.com/KB/winsdk/console_event_handling.aspx
+Good links:
+- http://www.codeproject.com/KB/winsdk/console_event_handling.aspx: how
+ to handle the events in foreground/console mode
+- the Sphynx searchd.cpp: especially service description, console events
+- POCO: on some design questions
+- Microsoft Knoledge Base: best source as always
+
diff --git a/include/wolf/log/messages.h b/include/wolf/log/messages.h
index ad706e7..0d0aa70 100644
--- a/include/wolf/log/messages.h
+++ b/include/wolf/log/messages.h
@@ -184,6 +184,8 @@
#define WOLF_MSG_SERVICE_CANT_CREATE_STOP_EVENT WOLF_MSG_SERVICE_BASE+15
#define WOLF_MSG_SERVICE_WAIT_FOR_OBJECT_FAILED WOLF_MSG_SERVICE_BASE+16
#define WOLF_MSG_SERVICE_CANT_DISPATCH_SERVICE WOLF_MSG_SERVICE_BASE+17
+#define WOLF_MSG_SERVICE_CANT_REGISTER_CONSOLE_CTRL_HANDLER WOLF_MSG_SERVICE_BASE+18
+#define WOLF_MSG_SERVICE_CONSOLE_CTRL_RECEIVED WOLF_MSG_SERVICE_BASE+19
#ifdef __cplusplus
extern "C" {
diff --git a/include/wolf/service/service.h b/include/wolf/service/service.h
index 2033117..2645bc3 100644
--- a/include/wolf/service/service.h
+++ b/include/wolf/service/service.h
@@ -116,7 +116,13 @@ typedef enum wolf_service_event_t {
*
* @return the event which happened
*/
-wolf_service_event_t wolf_service_events_suspend( int timeout, wolf_error_t *error );
+typedef wolf_service_event_t (*LPWOLF_SERVICE_SUSPEND_FUNCTION)( int timeout, wolf_error_t *error );
+
+/**
+ * The current version of the suspend function, depends whether we started the service
+ * in the console or really as service
+ */
+extern LPWOLF_SERVICE_SUSPEND_FUNCTION wolf_service_events_suspend;
#ifdef __cplusplus
}
diff --git a/src/service/service.c b/src/service/service.c
index bae331a..48a32e4 100644
--- a/src/service/service.c
+++ b/src/service/service.c
@@ -12,6 +12,8 @@ static SERVICE_STATUS service_status;
static HANDLE service_stop_event = NULL;
static LPSERVICE_MAIN_FUNCTION service_service_main = NULL;
+LPWOLF_SERVICE_SUSPEND_FUNCTION wolf_service_events_suspend = NULL;
+
#define SERVICE_NAME "testservice"
wolf_error_t wolf_service_install( wolf_service_params_t params ) {
@@ -317,18 +319,34 @@ void WINAPI wolf_service_main( DWORD argc, LPTSTR *argv ) {
return;
}
-wolf_service_event_t wolf_service_events_suspend( int timeout, wolf_error_t *error ) {
+wolf_service_event_t wolf_service_events_suspend_console( int timeout, wolf_error_t *error ) {
DWORD res;
char errbuf[512];
- /* non-service mode, return after timeout */
- /* TODO: how to catch stopping of console application and to send a terminate
- * event? */
- if( service_stop_event == NULL ) {
- Sleep( timeout );
- *error = WOLF_ERR_TIMEOUT;
- return WOLF_SERVICE_NO_EVENT;
+ res = WaitForSingleObject( service_stop_event, timeout * 1000 );
+ switch( res ) {
+ case WAIT_OBJECT_0:
+ /* stop signal received */
+ *error = WOLF_OK;
+ return WOLF_SERVICE_EVENT_TERMINATE;
+
+ case WAIT_TIMEOUT:
+ *error = WOLF_ERR_TIMEOUT;
+ return WOLF_SERVICE_NO_EVENT;
+
+ default:
+ WOLF_LOG_GET_LAST_ERROR( GetLastError( ), errbuf, 512 );
+ wolf_log( WOLF_LOG_ERR, WOLF_CATEGORY_SERVICE, WOLF_MSG_SERVICE_WAIT_FOR_OBJECT_FAILED,
+ _( "Waiting for events in the service '%s' failed: %s (%d)" ),
+ SERVICE_NAME, errbuf, GetLastError( ) );
+ *error = WOLF_ERR_INTERNAL;
+ return WOLF_SERVICE_NO_EVENT;
}
+}
+
+wolf_service_event_t wolf_service_events_suspend_service( int timeout, wolf_error_t *error ) {
+ DWORD res;
+ char errbuf[512];
res = WaitForSingleObject( service_stop_event, timeout * 1000 );
switch( res ) {
@@ -353,12 +371,77 @@ wolf_service_event_t wolf_service_events_suspend( int timeout, wolf_error_t *err
}
}
+/* constants and values from WinCon.h: */
+static const char *wolf_service_console_ctrl_to_str( DWORD ctrl ) {
+ switch( ctrl ) {
+ case CTRL_C_EVENT: return "CTRL_C_EVENT";
+ case CTRL_BREAK_EVENT: return "CTRL_BREAK_EVENT";
+ case CTRL_CLOSE_EVENT: return "CTRL_CLOSE_EVENT";
+ case CTRL_LOGOFF_EVENT: return "CTRL_LOGOFF_EVENT";
+ case CTRL_SHUTDOWN_EVENT: return "CTRL_SHUTDOWN_EVENT";
+ default: return "<unknown control>";
+ }
+}
+
+static BOOL WINAPI wolf_service_console_ctrl_handler( DWORD ctrl ) {
+ wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_SERVICE, WOLF_MSG_SERVICE_CONSOLE_CTRL_RECEIVED,
+ _( "Got a console control event '%s' (%d)" ),
+ wolf_service_console_ctrl_to_str( ctrl ), ctrl );
+
+ switch( ctrl ) {
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ case CTRL_CLOSE_EVENT:
+ case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ SetEvent( service_stop_event );
+ return TRUE;
+ }
+
+ return FALSE;
+}
wolf_error_t wolf_service_start_console( LPTSTR service_name,
LPSERVICE_MAIN_FUNCTION service_main,
DWORD argc,
LPTSTR *argv ) {
- service_main( argc, argv );
+ BOOL res;
+ char errbuf[512];
+
+ /* register the user-defined service main (what he should actually program */
+ service_service_main = service_main;
+
+ /* register the right suspend function */
+ wolf_service_events_suspend = wolf_service_events_suspend_console;
+
+ /* register a stop event, the console control handler will send
+ * the event.
+ */
+ service_stop_event = CreateEvent(
+ NULL, /* default security attributes */
+ TRUE, /* manual reset event */
+ FALSE, /* not signalled */
+ NULL ); /* no name */
+ if( service_stop_event == NULL ) {
+ WOLF_LOG_GET_LAST_ERROR( GetLastError( ), errbuf, 512 );
+ wolf_log( WOLF_LOG_ERR, WOLF_CATEGORY_SERVICE, WOLF_MSG_SERVICE_CANT_CREATE_STOP_EVENT,
+ _( "Unable to create the stop event for service '%s': %s (%d)" ),
+ SERVICE_NAME, errbuf, GetLastError( ) );
+ return WOLF_ERR_INTERNAL;
+ }
+
+ /* catch console events for proper termination of the service in console mode */
+ res = SetConsoleCtrlHandler( wolf_service_console_ctrl_handler, TRUE );
+ if( !res ) {
+ WOLF_LOG_GET_LAST_ERROR( GetLastError( ), errbuf, 512 );
+ wolf_log( WOLF_LOG_ERR, WOLF_CATEGORY_SERVICE, WOLF_MSG_SERVICE_CANT_REGISTER_CONSOLE_CTRL_HANDLER,
+ _( "Unable to register the console control handler for foreground service '%s': %s (%d)" ),
+ service_name, errbuf, GetLastError( ) );
+ return WOLF_ERR_INTERNAL;
+ }
+
+ /* now call the user-defined service main function */
+ service_service_main( argc, argv );
return WOLF_OK;
}
@@ -374,6 +457,9 @@ wolf_error_t wolf_service_start( LPTSTR service_name,
/* register the user-defined service main (what he should actually program */
service_service_main = service_main;
+ /* register the right suspend function */
+ wolf_service_events_suspend = wolf_service_events_suspend_service;
+
/* register the wolf_service_main to the SCM, which is the entry point for
* the service (doing some things we want to hide from the service developer),
* and which in the end calls 'service_service_main' (the user-defined main
diff --git a/tests/service/testservice.c b/tests/service/testservice.c
index 896df51..478dbc4 100644
--- a/tests/service/testservice.c
+++ b/tests/service/testservice.c
@@ -88,7 +88,7 @@ void __cdecl _tmain( int argc, TCHAR *argv[] ) {
} else if( strcasecmp( argv[1], "/remove" ) == 0 ) {
(void)wolf_service_remove( SERVICE_NAME );
} else if( strcasecmp( argv[1], "/foreground" ) == 0 ) {
- wolf_service_start_console( SERVICE_NAME, &service_main, argc, argv );
+ (void)wolf_service_start_console( SERVICE_NAME, &service_main, argc, argv );
} else {
fprintf( stderr, "Illegal option '%s'\r\n", argv[1] );
}