-
Notifications
You must be signed in to change notification settings - Fork 7
/
ServiceThread.cpp
513 lines (426 loc) · 15.7 KB
/
ServiceThread.cpp
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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
//////////////////////////////////////////////////////////////////////////////
//
// ServiceThread.cpp
// Win32::Daemon Perl extension service manager thread source file
//
// Copyright (c) 1998-2008 Dave Roth
// Courtesy of Roth Consulting
// http://www.roth.net/
//
// This file may be copied or modified only under the terms of either
// the Artistic License or the GNU General Public License, which may
// be found in the Perl 5.0 source kit.
//
// 2008.03.24 :Date
// 20080324 :Version
//////////////////////////////////////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN
// Enable MS Visual Studio 2005's secure stdlib
// The _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES macro defined with a value of 1
// will swap out a secured version of stdlib functions. These perform buffer overrun
// checking and such.
// define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
// As an alternative you can keep the depricated function calls and block out any
// security warnings by defining the _CRT_SECURE_NO_DEPRECATE macro.
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <lmaccess.h> // Service stuff
#include <lmserver.h> // Service stuff
#include <lmapibuf.h>
#include <LMERR.H> // For the NERR_Succes macro
#include <stdio.h> // REmove. ONly used for sprintf for debugging.
#include "constant.h"
#include "CWinStation.hpp"
#include "ServiceThread.hpp"
// #include <crtdbg.h>
static SERVICE_TABLE_ENTRY gpServiceTable[] =
{
{
(char*)TEXT( "GROWL!" ), ServiceMain
},
{
NULL, NULL
}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ServiceMain()
// Called by the Service Manager.
VOID WINAPI ServiceMain( DWORD dwArgs, LPTSTR *ppszArgs)
{
HWND hWnd = NULL;
LPCTSTR pszServiceName = ppszArgs[0];
//
// Record this ServiceMain's thread ID for other threads to use.
//
gServiceMainThreadID = GetCurrentThreadId();
ALERT( "ServiceMain: function started. Passed inn Name is...\n" );
ALERT( pszServiceName );
ALERT( "ServiceMain: About to call RegisterServiceCtrlHandler()...\n" );
// Test to see if we can cause the message loop to start queuing...
SetTimeoutTimer( 10 );
CleanStatusStruct( &gServiceStatus );
gServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ghService = RegisterServiceCtrlHandler( pszServiceName, ServiceHandler );
if( 0 != ghService )
{
ALERT( "ServiceMain: Just came out of RegisterServiceCtrlHandler()" );
// If the state has not yet changed then push start everything...
if( 0 == gdwState )
{
gdwState = SERVICE_START_PENDING;
//
// If we are in callback mode then make sure to
// start by posting a SERVICE_START_PENDING message
// (even though one does not exist in the Win32 API)
// so the script has a chance to start.
//
if( FALSE != gfCallbackMode )
{
//
// Call the service handler indicating that the "fake"
// SERVICE_CONTROL_START event has been received.
//
ServiceHandler( SERVICE_CONTROL_START );
}
}
{
char szBuffer[256];
sprintf( szBuffer, "ServiceMain: About to call My_SetServiceBits with gdwServiceBits=0x%08x", gdwServiceBits );
ALERT( szBuffer );
}
if( 0 != gdwServiceBits )
{
My_SetServiceBits( ghService, gdwServiceBits, TRUE, TRUE );
}
ALERT( "ServiceMain: Entering message loop" );
// Call a Win32 User level function to create a message queue
GetDesktopWindow();
GetWindow( NULL, GW_HWNDFIRST );
if( 1 )
{
MSG Message;
BOOL fContinueProcessing = TRUE;
while( TRUE == fContinueProcessing )
{
ALERT( "ServiceMain: Just enetered the message loop" );
try
{
fContinueProcessing = (BOOL) GetMessage( &Message, (HWND) NULL, 0, 0 );
#ifdef _DEBUG
TCHAR szBuffer[256];
wsprintf( szBuffer, "Got message: 0x%08x", Message.message );
ALERT( szBuffer );
#endif // _DEBUG
}
catch (...)
{
ALERT( "ServiceMain: Ouch!!! We caught an exception!" );
}
switch( Message.message )
{
case WM_USER_SET_TIMER:
ALERT( "ServiceMain: Setting timer" );
ghTimer = ::SetTimer( NULL, SERVICE_THREAD_TIMER_ID, (UINT)Message.wParam * DEFAULT_HANDLER_TIMEOUT_SCALE, (TIMERPROC)TimerHandler );
break;
case WM_QUIT:
fContinueProcessing = FALSE;
break;
case WM_QUERYENDSESSION:
case WM_ENDSESSION:
case WM_TIMER:
ALERT( "ServiceMain: HandlerTimeoutTimer due to WM_TIMER." );
KillTimer();
gdwState = gdwTimeoutState;
UpdateServiceStatus( gdwTimeoutState );
default:
ALERT( "ServiceMain: Dispatching message." );
TranslateMessage( &Message );
//
// Calling DispatchMessage() is probably foolish since
// there is no window associated with this thread.
// Per MSDN: messages that are not associated with a window cannot be dispatched by the DispatchMessage function
DispatchMessage( &Message );
}
}
}
ALERT( "ServiceMain: Just left the message loop." );
UpdateServiceStatus( gdwState );
}
else
{
gdwState = SERVICE_STOPPED;
#ifdef _DEBUG
TCHAR szBuffer[ 100 ];
wsprintf( szBuffer, TEXT( "ServiceMain: ERROR! 0x08x" ), GetLastError() );
ALERT( szBuffer );
#endif // _DEBUG
}
ALERT( "ServiceMain: Shutting down ServiceMain()!" );
return;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID WINAPI ServiceHandler( DWORD dwControl )
{
const char *pszCommand = SERVICE_CONTROL_STRING_EMPTY;
DWORD dwState = gdwState;
BOOL fUseTimer = FALSE;
#ifdef _DEBUG
TCHAR szBuffer[ 256 ];
#endif // _DEBUG
ALERT( "ServiceHandler: Incoming service control message..." );
switch( dwControl )
{
case SERVICE_CONTROL_START:
pszCommand = SERVICE_CONTROL_STRING_START;
// This control message requires that we change the state...
dwState = SERVICE_START_PENDING;
fUseTimer = TRUE;
gdwTimeoutState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
pszCommand = SERVICE_CONTROL_STRING_STOP;
// This control message requires that we change the state...
dwState = SERVICE_STOP_PENDING;
fUseTimer = TRUE;
gdwTimeoutState = SERVICE_STOPPED;
break;
case SERVICE_CONTROL_PAUSE:
pszCommand = SERVICE_CONTROL_STRING_PAUSE;
// This control message requires that we change the state...
// ...but if we are already paused then don't
if( SERVICE_PAUSED != gdwState )
{
dwState = SERVICE_PAUSE_PENDING;
}
fUseTimer = TRUE;
gdwTimeoutState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
pszCommand = SERVICE_CONTROL_STRING_CONTINUE;
// This control message requires that we change the state...
// ...but only if we are already paused.
if( SERVICE_PAUSED == gdwState )
{
dwState = SERVICE_CONTINUE_PENDING;
}
fUseTimer = TRUE;
gdwTimeoutState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_SHUTDOWN:
pszCommand = SERVICE_CONTROL_STRING_SHUTDOWN;
// No dwState value for this control message
// No gdwTimeoutState for this state
break;
///////////////////////////////////////////////////////////////
// Start nonstates (these are commands)
// Fix by Thomas Kratz [[email protected]]
// Control command messages have not associated state
// so don't set the dwState
case SERVICE_CONTROL_INTERROGATE:
pszCommand = SERVICE_CONTROL_STRING_INTERROGATE;
gdwLastControlMessage = dwControl;
// No dwState value for this control message
// No gdwTimoutState for this state
break;
// Win2k control codes...
case SERVICE_CONTROL_PARAMCHANGE:
pszCommand = SERVICE_CONTROL_STRING_PARAMCHANGE;
// No dwState value for this control message
break;
case SERVICE_CONTROL_NETBINDADD:
pszCommand = SERVICE_CONTROL_STRING_NETBINDADD;
// No dwState value for this control message
break;
case SERVICE_CONTROL_NETBINDREMOVE:
pszCommand = SERVICE_CONTROL_STRING_NETBINDREMOVE;
// No dwState value for this control message
break;
case SERVICE_CONTROL_NETBINDENABLE:
pszCommand = SERVICE_CONTROL_STRING_NETBINDENABLE;
// No dwState value for this control message
break;
case SERVICE_CONTROL_NETBINDDISABLE:
pszCommand = SERVICE_CONTROL_STRING_NETBINDDISABLE;
// No dwState value for this control message
break;
#ifdef SERVICE_CONTROL_PRESHUTDOWN
case SERVICE_CONTROL_PRESHUTDOWN :
pszCommand = SERVICE_CONTROL_STRING_PRESHUTDOWN ;
// No dwState value for this control message
break;
#endif
// User defined control codes...there are 128 of them
case SERVICE_CONTROL_USER_DEFINED:
case SERVICE_CONTROL_USER_DEFINED + 0x01:
case SERVICE_CONTROL_USER_DEFINED + 0x4f:
// No dwState value for this control message
break;
default:
pszCommand = SERVICE_CONTROL_STRING_DEFAULT;
// No dwState value for this control message
break;
}
// Set the last control message to what was received. Some control messages
// result in a state change but we should always report the message.
gdwLastControlMessage = dwControl;
#ifdef _DEBUG
wsprintf( szBuffer, "ServiceHandler: Received message => %s (0x%0x)\n", pszCommand, dwControl );
ALERT( szBuffer );
#endif // _DEBUG
// TODO:
// We should set an alarm to for some configurable timeout value so that
// in case the perl script does not process the request we will change
// the state automatically
if( FALSE != fUseTimer )
{
SetTimeoutTimer( gdwHandlerTimeout );
}
//
// Update the service status with the dwState. If there were
// control messages that warrant a state change then do it
// otherwise dwState was set (at beginning of this function)
// to be the same as the current state.
//
UpdateServiceStatus( dwState );
//
// This code is only used when in callback mode...
// Post a daemon state change message to the main Win32::Daemon
// thread so that it knows to callback into Perl.
//
if( FALSE != gfCallbackMode )
{
ALERT( "ServiceHandler: Posting message to main thread for callbacks\n" );
//
// Post to the main thread that we have a state change.
//
// Make sure to post the dwControl value (not dwState). This control value
// maps directly to callback index values!
//
PostThreadMessage( gMainThreadId, WM_DAEMON_STATE_CHANGE, (WORD) dwControl, 0 );
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetTimeoutTimer( DWORD dwTimeout )
{
KillTimer();
// TODO:
// Make sure this code works. We are posting the set timer message to the service main thread...NOT
// the service thread.
PostThreadMessage( (DWORD) gServiceMainThreadID, WM_USER_SET_TIMER, (WPARAM) dwTimeout * DEFAULT_HANDLER_TIMEOUT_SCALE, (LPARAM) NULL );
#ifdef _DEBUG
TCHAR szBuffer[ 256 ];
wsprintf( szBuffer, "Setting timer will value of %d. Timer # is %d", gdwHandlerTimeout, ghTimer );
ALERT( szBuffer );
#endif // _DEBUG
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CleanStatusStruct( SERVICE_STATUS *pServiceStatus )
{
if( NULL != pServiceStatus )
{
ZeroMemory( pServiceStatus, sizeof( SERVICE_STATUS ) );
pServiceStatus->dwServiceType = gdwServiceType;
pServiceStatus->dwControlsAccepted = gdwControlsAccepted;
pServiceStatus->dwWin32ExitCode = NO_ERROR;
pServiceStatus->dwServiceSpecificExitCode = 0x00000000;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID CALLBACK TimerHandler( HWND hWnd, UINT uMsg, UINT uEventId, DWORD dwSystemTime )
{
ALERT( "HandlerTimeoutTimer callback called." );
KillTimer();
gdwState = gdwTimeoutState;
UpdateServiceStatus( gdwTimeoutState );
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL KillTimer()
{
BOOL fResult = FALSE;
if( ghTimer )
{
ALERT( "Killing timer due to a new pending command" );
fResult = ::KillTimer( NULL, ghTimer );
ghTimer = 0;
}
return( fResult );
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL UpdateServiceStatus( DWORD dwState, DWORD dwWaitHint, DWORD dwError )
{
BOOL fResult = FALSE;
SERVICE_STATUS Status;
CleanStatusStruct( &Status );
gdwState = Status.dwCurrentState = dwState;
KillTimer();
#ifdef _DEBUG
TCHAR szBuffer[ 256];
wsprintf( szBuffer, "UpdateServiceStatus: Updating service status to 0x%08x with hint of 0x%04x\n", gdwState, dwWaitHint );
ALERT( szBuffer );
#endif // _DEBUG
Status.dwWaitHint = dwWaitHint;
// If no error was specified then we must use the last error used.
if( 0xFFFFFFFF == dwError )
{
dwError = gdwServiceErrorState;
}
else
{
gdwServiceErrorState = dwError;
}
if( NO_ERROR == dwError )
{
Status.dwWin32ExitCode = NO_ERROR;
Status.dwServiceSpecificExitCode = 0;
}
else
{
Status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
Status.dwServiceSpecificExitCode = dwError;
}
fResult = SetServiceStatus( ghService, &Status );
#ifdef _DEBUG
if( fResult )
{
ALERT( "UpdateServiceStatus: update was successful" );
}
else
{
ALERT( "UpdateServiceStatus: update failed" );
}
#endif
return( fResult );
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI ServiceThread( LPVOID pVoid )
{
DWORD dwResult = 0;
ALERT( "ServiceThread: Starting. Calling StartServiceCtrlDispatcher()..." );
if( FALSE != StartServiceCtrlDispatcher( (LPSERVICE_TABLE_ENTRY) gpServiceTable ) )
{
// Successful
dwResult = 1;
}
else
{
dwResult = 0;
}
ALERT( "ServiceThread: Finished with StartServiceCtrlDispatcher()" );
UpdateServiceStatus( SERVICE_STOPPED );
ALERT( "ServiceThraed: ENDING THE SERVICE THREAD!!!!!!!!!!" );
return( dwResult );
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
HISTORY
20020605 rothd
- Modified: UpdateServiceStatus() function to accept a 3rd parameter (dwError).
This allows the calling code to report a service error.
20070102 rothd
- Cleaned up a bit.
- Added WM_QUIT message to the ServiceMain function. Now the Perl StopService() will
post this message to shut down the service thread.
- Fixed bug where messages were posted to wrong thread.
20080321 rothd
-Added support for SERVICE_CONTROL_PRESHUTDOWN.
*/