1 /*---------------------------------------------------------------------------
2 THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
3 ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
4 TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
5 PARTICULAR PURPOSE.
6
7 Copyright (C) Microsoft Corporation. All rights reserved.
8
9 MODULE: service.c
10
11 PURPOSE: Implements functions required by all Windows NT services
12
13 FUNCTIONS:
14 main(int argc, char **argv);
15 service_ctrl(DWORD dwCtrlCode);
16 service_main(DWORD dwArgc, LPTSTR *lpszArgv);
17 CmdInstallService();
18 CmdRemoveService();
19 CmdDebugService(int argc, char **argv);
20 ControlHandler ( DWORD dwCtrlType );
21 GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
22
23 ---------------------------------------------------------------------------*/
24 #include <windows.h>
25 #ifndef STANDALONE_NFSD
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <process.h>
29 #include <tchar.h>
30
31 #include "service.h"
32
33 // internal variables
34 SERVICE_STATUS ssStatus; // current status of the service
35 SERVICE_STATUS_HANDLE sshStatusHandle;
36 DWORD dwErr = 0;
37 BOOL bDebug = FALSE;
38 TCHAR szErr[256];
39
40 // internal function prototypes
41 VOID WINAPI service_ctrl(DWORD dwCtrlCode);
42 VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
43 VOID CmdInstallService();
44 VOID CmdRemoveService();
45 VOID CmdDebugService(int argc, char **argv);
46 BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
47 LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
48
49 //
50 // FUNCTION: main
51 //
52 // PURPOSE: entrypoint for service
53 //
54 // PARAMETERS:
55 // argc - number of command line arguments
56 // argv - array of command line arguments
57 //
58 // RETURN VALUE:
59 // none
60 //
61 // COMMENTS:
62 // main() either performs the command line task, or
63 // call StartServiceCtrlDispatcher to register the
64 // main service thread. When the this call returns,
65 // the service has stopped, so exit.
66 //
main(int argc,char ** argv)67 void __cdecl main(int argc, char **argv)
68 {
69 SERVICE_TABLE_ENTRY dispatchTable[] =
70 {
71 { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
72 { NULL, NULL}
73 };
74
75 if ( (argc > 1) &&
76 ((*argv[1] == '-') || (*argv[1] == '/')) )
77 {
78 if ( _stricmp( "install", argv[1]+1 ) == 0 )
79 {
80 CmdInstallService();
81 }
82 else if ( _stricmp( "remove", argv[1]+1 ) == 0 )
83 {
84 CmdRemoveService();
85 }
86 else if ( _stricmp( "debug", argv[1]+1 ) == 0 )
87 {
88 bDebug = TRUE;
89 CmdDebugService(argc, argv);
90 }
91 else
92 {
93 goto dispatch;
94 }
95 exit(0);
96 }
97
98 // if it doesn't match any of the above parameters
99 // the service control manager may be starting the service
100 // so we must call StartServiceCtrlDispatcher
101 dispatch:
102 // this is just to be friendly
103 printf( "%s -install to install the service\n", SZAPPNAME );
104 printf( "%s -remove to remove the service\n", SZAPPNAME );
105 printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
106 printf( "\nStartServiceCtrlDispatcher being called.\n" );
107 printf( "This may take several seconds. Please wait.\n" );
108
109 if (!StartServiceCtrlDispatcher(dispatchTable))
110 AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
111 }
112
113
114
115 //
116 // FUNCTION: service_main
117 //
118 // PURPOSE: To perform actual initialization of the service
119 //
120 // PARAMETERS:
121 // dwArgc - number of command line arguments
122 // lpszArgv - array of command line arguments
123 //
124 // RETURN VALUE:
125 // none
126 //
127 // COMMENTS:
128 // This routine performs the service initialization and then calls
129 // the user defined ServiceStart() routine to perform majority
130 // of the work.
131 //
service_main(DWORD dwArgc,LPTSTR * lpszArgv)132 void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
133 {
134
135 // register our service control handler:
136 //
137 sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
138
139 if (!sshStatusHandle)
140 goto cleanup;
141
142 // SERVICE_STATUS members that don't change in example
143 //
144 ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
145 ssStatus.dwServiceSpecificExitCode = 0;
146
147
148 // report the status to the service control manager.
149 //
150 if (!ReportStatusToSCMgr(
151 SERVICE_START_PENDING, // service state
152 NO_ERROR, // exit code
153 3000)) // wait hint
154 goto cleanup;
155
156 ServiceStart( dwArgc, lpszArgv );
157
158 cleanup:
159
160 // try to report the stopped status to the service control manager.
161 //
162 if (sshStatusHandle)
163 (VOID)ReportStatusToSCMgr(
164 SERVICE_STOPPED,
165 dwErr,
166 0);
167
168 return;
169 }
170
171
172
173 //
174 // FUNCTION: service_ctrl
175 //
176 // PURPOSE: This function is called by the SCM whenever
177 // ControlService() is called on this service.
178 //
179 // PARAMETERS:
180 // dwCtrlCode - type of control requested
181 //
182 // RETURN VALUE:
183 // none
184 //
185 // COMMENTS:
186 //
service_ctrl(DWORD dwCtrlCode)187 VOID WINAPI service_ctrl(DWORD dwCtrlCode)
188 {
189 // Handle the requested control code.
190 //
191
192 switch (dwCtrlCode)
193 {
194 // Stop the service.
195 //
196 // SERVICE_STOP_PENDING should be reported before
197 // setting the Stop Event - hServerStopEvent - in
198 // ServiceStop(). This avoids a race condition
199 // which may result in a 1053 - The Service did not respond...
200 // error.
201 case SERVICE_CONTROL_STOP:
202 ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
203 ServiceStop();
204 return;
205
206 // Update the service status.
207 //
208 case SERVICE_CONTROL_INTERROGATE:
209 break;
210
211 // invalid control code
212 //
213 default:
214 break;
215
216 }
217
218 ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
219 }
220
221
222
223 //
224 // FUNCTION: ReportStatusToSCMgr()
225 //
226 // PURPOSE: Sets the current status of the service and
227 // reports it to the Service Control Manager
228 //
229 // PARAMETERS:
230 // dwCurrentState - the state of the service
231 // dwWin32ExitCode - error code to report
232 // dwWaitHint - worst case estimate to next checkpoint
233 //
234 // RETURN VALUE:
235 // TRUE - success
236 // FALSE - failure
237 //
238 // COMMENTS:
239 //
ReportStatusToSCMgr(DWORD dwCurrentState,DWORD dwWin32ExitCode,DWORD dwWaitHint)240 BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
241 DWORD dwWin32ExitCode,
242 DWORD dwWaitHint)
243 {
244 static DWORD dwCheckPoint = 1;
245 BOOL fResult = TRUE;
246
247
248 if ( !bDebug ) // when debugging we don't report to the SCM
249 {
250 if (dwCurrentState == SERVICE_START_PENDING)
251 ssStatus.dwControlsAccepted = 0;
252 else
253 ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
254
255 ssStatus.dwCurrentState = dwCurrentState;
256 ssStatus.dwWin32ExitCode = dwWin32ExitCode;
257 ssStatus.dwWaitHint = dwWaitHint;
258
259 if ( ( dwCurrentState == SERVICE_RUNNING ) ||
260 ( dwCurrentState == SERVICE_STOPPED ) )
261 ssStatus.dwCheckPoint = 0;
262 else
263 ssStatus.dwCheckPoint = dwCheckPoint++;
264
265
266 // Report the status of the service to the service control manager.
267 fResult = SetServiceStatus(sshStatusHandle, &ssStatus);
268 if (!fResult)
269 AddToMessageLog(TEXT("SetServiceStatus"));
270 }
271 return fResult;
272 }
273
274
275
276 //
277 // FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
278 //
279 // PURPOSE: Allows any thread to log an error message
280 //
281 // PARAMETERS:
282 // lpszMsg - text for message
283 //
284 // RETURN VALUE:
285 // none
286 //
287 // COMMENTS:
288 //
AddToMessageLog(LPTSTR lpszMsg)289 VOID AddToMessageLog(LPTSTR lpszMsg)
290 {
291 TCHAR szMsg [(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100 ];
292 HANDLE hEventSource;
293 LPTSTR lpszStrings[2];
294
295 if ( !bDebug )
296 {
297 dwErr = GetLastError();
298
299 // Use event logging to log the error.
300 //
301 hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
302
303 #ifndef __REACTOS__
304 _stprintf_s(szMsg,(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
305 #else
306 _sntprintf(szMsg,(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
307 #endif
308 lpszStrings[0] = szMsg;
309 lpszStrings[1] = lpszMsg;
310
311 if (hEventSource != NULL)
312 {
313 ReportEvent(hEventSource, // handle of event source
314 EVENTLOG_ERROR_TYPE, // event type
315 0, // event category
316 0, // event ID
317 NULL, // current user's SID
318 2, // strings in lpszStrings
319 0, // no bytes of raw data
320 lpszStrings, // array of error strings
321 NULL); // no raw data
322
323 (VOID) DeregisterEventSource(hEventSource);
324 }
325 }
326 }
327
328
329
330
331 ///////////////////////////////////////////////////////////////////
332 //
333 // The following code handles service installation and removal
334 //
335
336
337 //
338 // FUNCTION: CmdInstallService()
339 //
340 // PURPOSE: Installs the service
341 //
342 // PARAMETERS:
343 // none
344 //
345 // RETURN VALUE:
346 // none
347 //
348 // COMMENTS:
349 //
CmdInstallService()350 void CmdInstallService()
351 {
352 SC_HANDLE schService;
353 SC_HANDLE schSCManager;
354
355 TCHAR szPath[512];
356
357 if ( GetModuleFileName( NULL, szPath, 512 ) == 0 )
358 {
359 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
360 return;
361 }
362
363 schSCManager = OpenSCManager(
364 NULL, // machine (NULL == local)
365 NULL, // database (NULL == default)
366 SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE // access required
367 );
368 if ( schSCManager )
369 {
370 schService = CreateService(
371 schSCManager, // SCManager database
372 TEXT(SZSERVICENAME), // name of service
373 TEXT(SZSERVICEDISPLAYNAME), // name to display
374 SERVICE_QUERY_STATUS, // desired access
375 SERVICE_WIN32_OWN_PROCESS, // service type
376 SERVICE_AUTO_START, // start type
377 SERVICE_ERROR_NORMAL, // error control type
378 szPath, // service's binary
379 NULL, // no load ordering group
380 NULL, // no tag identifier
381 TEXT(SZDEPENDENCIES), // dependencies
382 NULL, // LocalSystem account
383 NULL); // no password
384
385 if ( schService )
386 {
387 _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
388 CloseServiceHandle(schService);
389 }
390 else
391 {
392 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
393 }
394
395 CloseServiceHandle(schSCManager);
396 }
397 else
398 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
399 }
400
401
402
403 //
404 // FUNCTION: CmdRemoveService()
405 //
406 // PURPOSE: Stops and removes the service
407 //
408 // PARAMETERS:
409 // none
410 //
411 // RETURN VALUE:
412 // none
413 //
414 // COMMENTS:
415 //
CmdRemoveService()416 void CmdRemoveService()
417 {
418 SC_HANDLE schService;
419 SC_HANDLE schSCManager;
420
421 schSCManager = OpenSCManager(
422 NULL, // machine (NULL == local)
423 NULL, // database (NULL == default)
424 SC_MANAGER_CONNECT // access required
425 );
426 if ( schSCManager )
427 {
428 schService = OpenService(schSCManager, TEXT(SZSERVICENAME), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
429
430 if (schService)
431 {
432 // try to stop the service
433 if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
434 {
435 _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
436 Sleep( 1000 );
437
438 while ( QueryServiceStatus( schService, &ssStatus ) )
439 {
440 if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
441 {
442 _tprintf(TEXT("."));
443 Sleep( 1000 );
444 }
445 else
446 break;
447 }
448
449 if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
450 _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
451 else
452 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
453
454 }
455
456 // now remove the service
457 if ( DeleteService(schService) )
458 _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
459 else
460 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
461
462
463 CloseServiceHandle(schService);
464 }
465 else
466 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
467
468 CloseServiceHandle(schSCManager);
469 }
470 else
471 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
472 }
473
474
475
476
477 ///////////////////////////////////////////////////////////////////
478 //
479 // The following code is for running the service as a console app
480 //
481
482
483 //
484 // FUNCTION: CmdDebugService(int argc, char ** argv)
485 //
486 // PURPOSE: Runs the service as a console application
487 //
488 // PARAMETERS:
489 // argc - number of command line arguments
490 // argv - array of command line arguments
491 //
492 // RETURN VALUE:
493 // none
494 //
495 // COMMENTS:
496 //
CmdDebugService(int argc,char ** argv)497 void CmdDebugService(int argc, char ** argv)
498 {
499 DWORD dwArgc;
500 LPTSTR *lpszArgv;
501
502 #ifdef UNICODE
503 lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
504 if (NULL == lpszArgv)
505 {
506 // CommandLineToArvW failed!!
507 _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
508 return;
509 }
510 #else
511 dwArgc = (DWORD) argc;
512 lpszArgv = argv;
513 #endif
514
515 _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
516
517 SetConsoleCtrlHandler( ControlHandler, TRUE );
518
519 ServiceStart( dwArgc, lpszArgv );
520
521 #ifdef UNICODE
522 // Must free memory allocated for arguments
523
524 GlobalFree(lpszArgv);
525 #endif // UNICODE
526
527 }
528
529
530 //
531 // FUNCTION: ControlHandler ( DWORD dwCtrlType )
532 //
533 // PURPOSE: Handled console control events
534 //
535 // PARAMETERS:
536 // dwCtrlType - type of control event
537 //
538 // RETURN VALUE:
539 // True - handled
540 // False - unhandled
541 //
542 // COMMENTS:
543 //
ControlHandler(DWORD dwCtrlType)544 BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
545 {
546 switch ( dwCtrlType )
547 {
548 case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
549 case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
550 _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
551 ServiceStop();
552 return TRUE;
553 break;
554
555 }
556 return FALSE;
557 }
558
559 //
560 // FUNCTION: GetLastErrorText
561 //
562 // PURPOSE: copies error message text to string
563 //
564 // PARAMETERS:
565 // lpszBuf - destination buffer
566 // dwSize - size of buffer
567 //
568 // RETURN VALUE:
569 // destination buffer
570 //
571 // COMMENTS:
572 //
GetLastErrorText(LPTSTR lpszBuf,DWORD dwSize)573 LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
574 {
575 DWORD dwRet;
576 LPTSTR lpszTemp = NULL;
577
578 dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
579 NULL,
580 GetLastError(),
581 LANG_NEUTRAL,
582 (LPTSTR)&lpszTemp,
583 0,
584 NULL );
585
586 // supplied buffer is not long enough
587 if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
588 lpszBuf[0] = TEXT('\0');
589 else
590 {
591 if (NULL != lpszTemp)
592 {
593 lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
594 #ifndef __REACTOS__
595 _stprintf_s( lpszBuf, dwSize, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
596 #else
597 _sntprintf( lpszBuf, dwSize, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
598 #endif
599 }
600 }
601
602 if ( NULL != lpszTemp )
603 LocalFree((HLOCAL) lpszTemp );
604
605 return lpszBuf;
606 }
607 #endif
608