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 // 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 // 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 // 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 // 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 // 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 // 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 // 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 // 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 // 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 // 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