summaryrefslogtreecommitdiff
path: root/tests/service/testservice.c
blob: e229704927a83b829fb8cab3718dabaef07dd670 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#include "log/log.h"			/* for logging */
#include "log/messages.h"		/* for i18n */
#include "port/string.h"		/* for strcasecmp */
#include "port/gettext.h"		/* for i18n */
#include "errors.h"

#include <windows.h>
#include <tchar.h>
#include <WinSvc.h>
#include <stdio.h>

#define WOLF_CATEGORY_TESTSERVICE	WOLF_LAST_INTERNAL_CATEGORY+1

#define WOLF_MSG_TESTSERVICE_BASE	( WOLF_CATEGORY_TESTSERVICE ) * 1000

#define WOLF_MSG_TESTSERVICE_CANT_DISPATCH_SERVICE	WOLF_MSG_TESTSERVICE_BASE+11
#define WOLF_MSG_TESTSERVICE_CANT_REGISTER_SERVICE_CTRL_HANDLER	WOLF_MSG_TESTSERVICE_BASE+12
#define WOLF_MSG_TESTSERVICE_HANDLING_EVENT		WOLF_MSG_TESTSERVICE_BASE+13
#define WOLF_MSG_TESTSERVICE_STARTED_SERVICE		WOLF_MSG_TESTSERVICE_BASE+14
#define WOLF_MSG_TESTSERVICE_STOPPED_SERVICE		WOLF_MSG_TESTSERVICE_BASE+15
#define WOLF_MSG_TESTSERVICE_CANT_CREATE_STOP_EVENT	WOLF_MSG_TESTSERVICE_BASE+16
#define WOLF_MSG_TESTSERVICE_CANT_REPORT_STATUS		WOLF_MSG_TESTSERVICE_BASE+17
#define WOLF_MSG_TESTSERVICE_WAIT_FOR_OBJECT_FAILED	WOLF_MSG_TESTSERVICE_BASE+18
#define WOLF_MSG_TESTSERVICE_REPORT_STATUS		WOLF_MSG_TESTSERVICE_BASE+19

#define SERVICE_NAME "testservice"
#define SERVICE_NAME_DESCR "Wolf Test Service"

/* FIXME: should be passed in the event contect as struct */
static SERVICE_STATUS_HANDLE service_status_handle;
static SERVICE_STATUS service_status;
static HANDLE service_stop_event = NULL;

/* constants and values from WinSvc.h: */
static const char *wolf_service_state_to_str(	DWORD current_state ) {
	switch( current_state ) {
		case SERVICE_STOPPED:		return "SERVICE_STOPPED";
		case SERVICE_START_PENDING:	return "SERVICE_START_PENDING";
		case SERVICE_STOP_PENDING:	return "SERVICE_STOP_PENDING";
		case SERVICE_RUNNING:		return "SERVICE_RUNNING";
		case SERVICE_CONTINUE_PENDING:	return "SERVICE_CONTINUE_PENDING";
		case SERVICE_PAUSE_PENDING:	return "SERVICE_PAUSE_PENDING";
		case SERVICE_PAUSED:		return "SERVICE_PAUSED";
		default:			return "<unknown service state>";
	}
}

/* constants and values from WinSvc.h: */
static const char *wolf_service_control_to_str(	DWORD control ) {
	switch( control ) {
		case SERVICE_CONTROL_STOP:		return "SERVICE_CONTROL_STOP";
		case SERVICE_CONTROL_PAUSE:		return "SERVICE_CONTROL_PAUSE";
		case SERVICE_CONTROL_CONTINUE:		return "SERVICE_CONTROL_CONTINUE";
		case SERVICE_CONTROL_INTERROGATE:	return "SERVICE_CONTROL_INTERROGATE";
		case SERVICE_CONTROL_SHUTDOWN:		return "SERVICE_CONTROL_SHUTDOWN";
		case SERVICE_CONTROL_PARAMCHANGE:	return "SERVICE_CONTROL_PARAMCHANGE";
		case SERVICE_CONTROL_NETBINDADD:	return "SERVICE_CONTROL_NETBINDADD";
		case SERVICE_CONTROL_NETBINDREMOVE:	return "SERVICE_CONTROL_NETBINDREMOVE";
		case SERVICE_CONTROL_NETBINDENABLE:	return "SERVICE_CONTROL_NETBINDENABLE";
		case SERVICE_CONTROL_NETBINDDISABLE:	return "SERVICE_CONTROL_NETBINDDISABLE";
		case SERVICE_CONTROL_DEVICEEVENT:	return "SERVICE_CONTROL_DEVICEEVENT";
		case SERVICE_CONTROL_HARDWAREPROFILECHANGE: return "SERVICE_CONTROL_HARDWAREPROFILECHANGE";
		case SERVICE_CONTROL_POWEREVENT:	return "SERVICE_CONTROL_POWEREVENT";
		case SERVICE_CONTROL_SESSIONCHANGE:	return "SERVICE_CONTROL_SESSIONCHANGE";
		default:				return "<unknown service control>";
	}
}

static void wolf_service_report_status(	DWORD current_state,
					DWORD exit_code,
					DWORD wait_hint ) {
	BOOL res;
	char errbuf[512];

	wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_TESTSERVICE, WOLF_MSG_TESTSERVICE_REPORT_STATUS,
		_( "reporting status with new state '%s' (%d) (currently in state '%s' (%d))" ),
		wolf_service_state_to_str( current_state ), current_state,
		wolf_service_state_to_str( service_status.dwCurrentState ), service_status.dwCurrentState );

	service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	service_status.dwCurrentState = current_state;
	service_status.dwWin32ExitCode = exit_code;
	service_status.dwServiceSpecificExitCode = 0;
	service_status.dwWaitHint = wait_hint;
	service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;

	switch( current_state ) {
		case SERVICE_START_PENDING:
			/* during startup we should not accept events, otherwise our
			 * state machine could get troubled!
			 */
			service_status.dwControlsAccepted = 0;
			break;

		case SERVICE_RUNNING:
		case SERVICE_STOPPED:
			/* reset checkpoint for status bar in final states */
			service_status.dwCheckPoint = 0;
			break;

		defaut:
			/* increate the tick for transient states */
			service_status.dwCheckPoint++;
			break;
	}

	res = SetServiceStatus( service_status_handle, &service_status );
	if( !res ) {
		WOLF_LOG_GET_LAST_ERROR( GetLastError( ), errbuf, 512 );
		wolf_log( WOLF_LOG_ERR, WOLF_CATEGORY_TESTSERVICE, WOLF_MSG_TESTSERVICE_CANT_REPORT_STATUS,
			_( "Unable to report state '%s' (%d) of service '%s' to SCM '%s (%d)" ),
			wolf_service_state_to_str( current_state ), current_state,
			SERVICE_NAME, errbuf, GetLastError( ) );
		return;
	}
}

/* we get called here by the system and the service control manager,
 * be as short as possible, report states and trigger new events at
 * most!
 */
void WINAPI wolf_service_ctrl_handler(	DWORD control ) {
	char errbuf[512];
	BOOL res;

	wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_TESTSERVICE, WOLF_MSG_TESTSERVICE_HANDLING_EVENT,
		_( "service handler received status change '%s' (%d)" ),
		wolf_service_control_to_str( control ),
		control );

	switch( control ) {
		case SERVICE_CONTROL_STOP:
			wolf_service_report_status( SERVICE_STOP_PENDING, NO_ERROR, 1000 );
			SetEvent( service_stop_event );
			break;

		case SERVICE_CONTROL_INTERROGATE:
			/* fall through to send current status */
			break;
	}

	/* report current state */
	wolf_service_report_status( service_status.dwCurrentState, NO_ERROR, 1000 );
}


void WINAPI wolf_service_main( DWORD argc, LPTSTR *argv ) {
	char errbuf[512];
	BOOL res;

	/* all other loggers make no sense: a service is not supposed
	 * to have a logfile or interaction with the user in any way!
	 */
	wolf_log_openlogtoeventlog( NULL, "Application", "testservice",
		"C:\\Temp\\testservicemsg.dll", WOLF_LAST_INTERNAL_CATEGORY+1, WOLF_LOG_DEBUG );

	/* register the event callback where we get called by the service
	 * manager and the system
	 */
	service_status_handle = RegisterServiceCtrlHandler(
		SERVICE_NAME, wolf_service_ctrl_handler );
	
	if( service_status_handle == 0 ) {
		WOLF_LOG_GET_LAST_ERROR( GetLastError( ), errbuf, 512 );
		wolf_log( WOLF_LOG_ERR, WOLF_CATEGORY_TESTSERVICE, WOLF_MSG_TESTSERVICE_CANT_REGISTER_SERVICE_CTRL_HANDLER,
			_( "Unable to register service control handler function for service '%s': %s (%d)" ),
			SERVICE_NAME, errbuf, GetLastError( ) );
		return;
	}

	/* signal that we are now up and running */
	wolf_service_report_status( SERVICE_START_PENDING, NO_ERROR, 3000 );

	/* register a stop event, the service control handler will send
	 * the event. From now on we have a service event handler installed,
	 * so if something goes wrong we can set the service state to
	 * SERVICE_STOPPED and terminate gracefully.
	 */
	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_TESTSERVICE, WOLF_MSG_TESTSERVICE_CANT_CREATE_STOP_EVENT,
			_( "Unable to create the stop event for service '%s': %s (%d)" ),
			SERVICE_NAME, errbuf, GetLastError( ) );
		wolf_service_report_status( SERVICE_STOPPED, NO_ERROR, 1000 );
	}

	wolf_service_report_status( SERVICE_RUNNING, NO_ERROR, 1000 );
	wolf_log( WOLF_LOG_NOTICE, WOLF_CATEGORY_TESTSERVICE, WOLF_MSG_TESTSERVICE_STARTED_SERVICE,
		_( "Started %s service" ), SERVICE_NAME );

	/* main loop */
	while( 1 ) {
		DWORD r;
		r = WaitForSingleObject( service_stop_event, 1000 );
		switch( r ) {
			case WAIT_OBJECT_0:
				/* stop signal received */
				goto SERVICE_END;

			case WAIT_TIMEOUT:
				/* we can do periodic things here */
				break;

			default:
				WOLF_LOG_GET_LAST_ERROR( GetLastError( ), errbuf, 512 );
				wolf_log( WOLF_LOG_ERR, WOLF_CATEGORY_TESTSERVICE, WOLF_MSG_TESTSERVICE_WAIT_FOR_OBJECT_FAILED,
					_( "Waiting for the stop event for service '%s' failed: %s (%d)" ),
					SERVICE_NAME, errbuf, GetLastError( ) );
				wolf_service_report_status( SERVICE_STOPPED, NO_ERROR, 1000 );
				return;
		}
	}

SERVICE_END:
	wolf_log( WOLF_LOG_NOTICE, WOLF_CATEGORY_TESTSERVICE, WOLF_MSG_TESTSERVICE_STOPPED_SERVICE,
		_( "Stopped %s service" ), SERVICE_NAME );

	wolf_service_report_status( SERVICE_STOPPED, NO_ERROR, 1000 );

	wolf_log_closelogtoeventlog( );

	return;
}

void __cdecl _tmain( int argc, TCHAR *argv[] ) {
	char errbuf[512];

	wolf_log_openlogtostderr( WOLF_LOG_DEBUG );
	wolf_log_openlogtofile( "testservice.log", WOLF_LOG_DEBUG );
	wolf_log_openlogtoeventlog( NULL, "Application", "testservice",
		"C:\\Temp\\testservicemsg.dll", WOLF_LAST_INTERNAL_CATEGORY+1, WOLF_LOG_DEBUG );

	/* called as service, dispatch the main service thread */
	if( argc < 2 ) {
		SERVICE_TABLE_ENTRY dispatch_table[2] =
			{ { SERVICE_NAME, wolf_service_main },
			  { NULL, NULL } };
		BOOL res;

		res = StartServiceCtrlDispatcher( dispatch_table );
		if( !res ) {
			WOLF_LOG_GET_LAST_ERROR( GetLastError( ), errbuf, 512 );
			wolf_log( WOLF_LOG_ERR, WOLF_CATEGORY_TESTSERVICE, WOLF_MSG_TESTSERVICE_CANT_DISPATCH_SERVICE,
				_( "Unable to dispatch service '%s': %s (%d)" ),
				SERVICE_NAME, errbuf, GetLastError( ) );
		}

		return;
	}

	/* not as service: provide functions like installation and
	 * deinstallation of the service. Also starting or stopping
	 * is an option here for easy handling from the shell.
	 */
	if( strcasecmp( argv[1], "/help" ) == 0 ) {
		printf( "testsrevice [options]\r\n" );
		printf( "  /help       show this help page\r\n" );
		printf( "  /install    install the service\r\n" );
		printf( "  /remove     remove the service\r\n" );
	} else if( strcasecmp( argv[1], "/install" ) == 0 ) {
		(void)wolf_service_install( SERVICE_NAME, SERVICE_NAME_DESCR );
	} else if( strcasecmp( argv[1], "/remove" ) == 0 ) {
		(void)wolf_service_remove( SERVICE_NAME );
	} else {
		fprintf( stderr, "Illegal option '%s'\r\n", argv[1] );
	}

	wolf_log_closelogtoeventlog( );
	wolf_log_closelogtofile( );
	wolf_log_closelogtostderr( );
}