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