diff options
author | Andreas Baumann <abaumann@yahoo.com> | 2009-03-27 18:31:27 +0100 |
---|---|---|
committer | Andreas Baumann <abaumann@yahoo.com> | 2009-03-27 18:31:27 +0100 |
commit | baa035aa68b7b72fe301c933227aa2530bbec66d (patch) | |
tree | d42e0636d34289a4c00645f5cc115e26e0bb91e0 | |
parent | f83210c88e7376e12b5cf0e86a073c59e4185efb (diff) | |
download | wolfbones-baa035aa68b7b72fe301c933227aa2530bbec66d.tar.gz wolfbones-baa035aa68b7b72fe301c933227aa2530bbec66d.tar.bz2 |
added service termination on console control events
-rw-r--r-- | docs/service/README | 8 | ||||
-rw-r--r-- | include/wolf/log/messages.h | 2 | ||||
-rw-r--r-- | include/wolf/service/service.h | 8 | ||||
-rw-r--r-- | src/service/service.c | 104 | ||||
-rw-r--r-- | tests/service/testservice.c | 2 |
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] ); } |