/*++ Copyright (c) 1990-91 Microsoft Corporation Module Name: srvmain.c Abstract: This is the main routine for the NT LAN Manager Server Service. !!! Does service controller guarantee no controls will be issued while we are initializing? Also, does it serialize controls? If not, we need some synchronization in here. Author: David Treadwell (davidtr) 05-10-1991 Revision History: 19-Jan-1993 Danl Removed the old long endpoint name "LanmanServer". 07-Jan-1993 Danl Added an RPC endpoint name using "srvsvc", since "LanmanServer" is too long for DOS machines to _access. For a short time we will support both names. 18-Feb-1992 ritaw Convert to Win32 service control APIs. --*/ #include "srvsvcp.h" #include #include #include #include #include #include #include #include // NetpNtStatusToApiStatus #include // NetpKdPrint #include #include #include // SERVER_INTERFACE_NAME #include #include SERVICE_STATUS SsServiceStatus; SERVICE_STATUS_HANDLE SsServiceStatusHandle; DWORD WINAPI ControlResponse( DWORD fdwControl, DWORD fdwEventType, LPVOID lpEventData, LPVOID lpContext ); VOID SvchostPushServiceGlobals( PSVCHOST_GLOBAL_DATA pGlobals ) { SsData.SsLmsvcsGlobalData = pGlobals; } VOID ServiceMain( IN DWORD argc, IN LPWSTR argv[] ) /*++ Routine Description: This is the "main" routine for the server service. The containing process will call this routine when we're supposed to start up. Arguments: Return Value: None. --*/ { RPC_STATUS rpcStatus; NET_API_STATUS error; NET_API_STATUS terminationError; BOOLEAN rpcServerStarted = FALSE; NTSTATUS Status; HANDLE EventHandle; OBJECT_ATTRIBUTES EventAttributes; UNICODE_STRING EventNameString; LARGE_INTEGER LocalTimeout; PSVCHOST_GLOBAL_DATA pTempGlobals = SsData.SsLmsvcsGlobalData; RtlZeroMemory( &SsData, sizeof( SsData ) ); RtlZeroMemory( &SsServiceStatus, sizeof( SsServiceStatus ) ); SsData.SsLmsvcsGlobalData = pTempGlobals; SsServiceStatusHandle = 0; SsInitializeServerInfoFields(); // // Make sure svchost.exe gave us the global data // ASSERT(SsData.SsLmsvcsGlobalData != NULL); // // Skip the Service Name in the argument list. // if (argc > 0) { argc--; if (argc > 0) { argv = &(argv[1]); } } #if DBG // // Set up for debugging--the first command line argument may be // "/debug:X" where SsDebug gets set to X. // if ( argc > 0 && STRNICMP( TEXT("/debug:"), (LPWSTR)argv[0], 7 ) == 0 ) { #ifdef UNICODE UNICODE_STRING ustr; RtlInitUnicodeString( &ustr, (PWSTR)argv[0] + 7 ); RtlUnicodeStringToInteger( &ustr, 16, &SsDebug ); #else SsDebug = 0; RtlCharToInteger( argv[0] + 7, 16, &SsDebug ); #endif } #ifndef USE_DEBUGGER //SsDebug = 0xffff; if ( SsDebug != 0 ) { CONSOLE_SCREEN_BUFFER_INFO csbi; COORD coord; (VOID)AllocConsole( ); (VOID)GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &csbi ); coord.X = (SHORT)(csbi.srWindow.Right - csbi.srWindow.Left + 1); coord.Y = (SHORT)((csbi.srWindow.Bottom - csbi.srWindow.Top + 1) * 20); (VOID)SetConsoleScreenBufferSize( GetStdHandle(STD_OUTPUT_HANDLE), coord ); } #endif #endif IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SRVSVC_main: server service starting.\n" )); } IF_DEBUG(INITIALIZATION_BREAKPOINT) { DbgUserBreakPoint( ); } // // Initialize all the status fields so that subsequent calls to // SetServiceStatus need to only update fields that changed. // SsServiceStatus.dwServiceType = SERVICE_WIN32; SsServiceStatus.dwCurrentState = SERVICE_START_PENDING; SsServiceStatus.dwControlsAccepted = 0; SsServiceStatus.dwCheckPoint = 1; SsServiceStatus.dwWaitHint = 30000; // 30 seconds SET_SERVICE_EXITCODE( NO_ERROR, SsServiceStatus.dwWin32ExitCode, SsServiceStatus.dwServiceSpecificExitCode ); // // Initialize server to receive service requests by registering the // control handler. // SsServiceStatusHandle = RegisterServiceCtrlHandlerEx( SERVICE_SERVER, ControlResponse, NULL ); if ( SsServiceStatusHandle == 0 ) { error = GetLastError(); IF_DEBUG(INITIALIZATION_ERRORS) { SS_PRINT(( "SRVSVC_main: RegisterServiceCtrlHandler failed: " "%ld\n", error )); } goto exit; } IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SRVSVC_main: Control handler registered.\n" )); } // // Wait for the Sam service to start. // // Later, when we initialize the server driver, it is going to create a // "NULL Session" token by calling LsaLogonUser. That call waits until // SAM is initialized. However, we don't have an opportunity to give // wait hints to the service controller, so we'll wait here. // // Create the event to wait on. // RtlInitUnicodeString( &EventNameString, L"\\SAM_SERVICE_STARTED" ); InitializeObjectAttributes( &EventAttributes, &EventNameString, 0, 0, NULL); Status = NtCreateEvent( &EventHandle, SYNCHRONIZE, &EventAttributes, NotificationEvent, (BOOLEAN) FALSE // The event is initially not signaled ); if ( !NT_SUCCESS(Status)) { // // If the event already exists, SAM beat us to creating it. // Just open it. // if( Status == STATUS_OBJECT_NAME_EXISTS || Status == STATUS_OBJECT_NAME_COLLISION ) { Status = NtOpenEvent( &EventHandle, SYNCHRONIZE, &EventAttributes ); } if ( !NT_SUCCESS(Status)) { error = NetpNtStatusToApiStatus(Status); IF_DEBUG(INITIALIZATION_ERRORS) { SS_PRINT(( "SRVSVC_main: Can't open SAM_SERVICE_STARTED event: %lx\n", Status )); } goto exit; } } // // Wait for SAM to finish initializing. // LocalTimeout = RtlEnlargedIntegerMultiply( SsServiceStatus.dwWaitHint/2, -10000 ); do { IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SRVSVC_main: Wait for SAM to init.\n" )); } AnnounceServiceStatus( 1 ); Status = NtWaitForSingleObject( EventHandle, (BOOLEAN)FALSE, &LocalTimeout); } while ( Status == STATUS_TIMEOUT ); (VOID) NtClose( EventHandle ); if ( !NT_SUCCESS(Status) ) { error = NetpNtStatusToApiStatus(Status); IF_DEBUG(INITIALIZATION_ERRORS) { SS_PRINT(( "SRVSVC_main: Wait for SAM_SERVICE_STARTED event failed: %lx\n", Status )); } goto exit; } IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SRVSVC_main: Done waiting for SAM to init.\n" )); } AnnounceServiceStatus( 1 ); // // Initialize server service data and the Lanman server FSP in kernel // mode. // error = SsInitialize( argc, argv ); if ( error != NO_ERROR ) { goto exit; } // // Set the variable that indicates that the server is fully // initialized. // SS_ASSERT( !SsData.SsInitialized ); SsData.SsInitialized = TRUE; // // Start the RPC server. Because other services may reside in this // process, the actual RPC server may already have been started; // this routine will track this for us. // rpcStatus = SsData.SsLmsvcsGlobalData->StartRpcServer( SERVER_INTERFACE_NAME, srvsvc_ServerIfHandle ); if ( rpcStatus != 0 ) { IF_DEBUG(INITIALIZATION_ERRORS) { SS_PRINT(( "SRVSVC_main: NetpStartRpcServer failed: %X\n", rpcStatus )); } error = rpcStatus; goto exit; } IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SRVSVC_main: RPC server started.\n" )); } rpcServerStarted = TRUE; // // Start getting PNP transport notifications from the server // error = StartPnpNotifications(); if( error != NO_ERROR ) { goto exit; } // // Announce that we have successfully started. // SsServiceStatus.dwCurrentState = SERVICE_RUNNING; SsServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN; SsServiceStatus.dwCheckPoint = 0; SsServiceStatus.dwWaitHint = 0; AnnounceServiceStatus( 0 ); IF_DEBUG(INITIALIZATION) { SS_PRINT(( "SRVSVC_main: initialization successfully completed.\n" )); } if (!I_ScSetServiceBits(SsServiceStatusHandle, SV_TYPE_SERVER, TRUE, TRUE, FALSE)) { error = GetLastError(); IF_DEBUG(INITIALIZATION_ERRORS) { SS_PRINT(( "SRVSVC_main: I_ScSetServiceBits failed: %ld\n", error )); } goto exit; } // // Use this thread as the scavenger thread to send server // announcements and watch the registry for configuration changes. // SS_ASSERT( SsData.SsInitialized ); (VOID)SsScavengerThread( NULL ); SS_ASSERT( SsData.SsInitialized ); exit: IF_DEBUG(TERMINATION) { SS_PRINT(( "SRVSVC_main: terminating.\n" )); } IF_DEBUG(TERMINATION_BREAKPOINT) { DbgUserBreakPoint( ); } // // Set the initialization variable to indicate that the server // service is not started. // SsData.SsInitialized = FALSE; // // Shut down our connection to the RPC server, if the RPC server // was started successfully. // if ( rpcServerStarted ) { rpcStatus = SsData.SsLmsvcsGlobalData->StopRpcServer ( srvsvc_ServerIfHandle ); if ( rpcStatus != NO_ERROR ) { IF_DEBUG(TERMINATION_ERRORS) { SS_PRINT(( "SRVSVC_main: unable to terminate RPC server: %X\n", rpcStatus )); } } else { IF_DEBUG(TERMINATION) { SS_PRINT(( "SRVSVC_main: RPC server successfully shut down.\n" )); } } } // // Announce that we're going down. // terminationError = error; SsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; SsServiceStatus.dwCheckPoint = 1; SsServiceStatus.dwWaitHint = 20000; // 20 seconds SET_SERVICE_EXITCODE( terminationError, SsServiceStatus.dwWin32ExitCode, SsServiceStatus.dwServiceSpecificExitCode ); AnnounceServiceStatus( 0 ); // // Clean up previously initialized state. // IF_DEBUG(TERMINATION) { SS_PRINT(( "SRVSVC_main: cleaning up.\n" )); } error = SsTerminate( ); if ( terminationError == NO_ERROR ) { terminationError = error; } // // Announce that we're down. // SsServiceStatus.dwCurrentState = SERVICE_STOPPED; SsServiceStatus.dwControlsAccepted = 0; SsServiceStatus.dwCheckPoint = 0; SsServiceStatus.dwWaitHint = 0; SET_SERVICE_EXITCODE( terminationError, SsServiceStatus.dwWin32ExitCode, SsServiceStatus.dwServiceSpecificExitCode ); AnnounceServiceStatus( 0 ); IF_DEBUG(TERMINATION) { SS_PRINT(( "SRVSVC_main: the server service is terminated.\n" )); } return; } // SVCS_ENTRY_POINT (SRVSVC_main) VOID AnnounceServiceStatus ( DWORD increment ) /*++ Routine Description: Announces the service's status to the service controller. Add 'increment' to the checkpoint value. Arguments: None. Return Value: None. --*/ { // // Service status handle is NULL if RegisterServiceCtrlHandler failed. // if ( SsServiceStatusHandle == 0 ) { SS_PRINT(( "AnnounceServiceStatus: Cannot call SetServiceStatus, " "no status handle.\n" )); return; } if( SsServiceStatus.dwCurrentState == SERVICE_RUNNING && increment ) { // // No need to tell the service controller about another checkpoint // since it already knows we're running // return; } SsServiceStatus.dwCheckPoint += increment; IF_DEBUG(ANNOUNCE) { SS_PRINT(( "AnnounceServiceStatus: CurrentState %lx\n" " ControlsAccepted %lx\n" " Win32ExitCode %lu\n" " ServiceSpecificExitCode %lu\n" " CheckPoint %lu\n" " WaitHint %lu\n", SsServiceStatus.dwCurrentState, SsServiceStatus.dwControlsAccepted, SsServiceStatus.dwWin32ExitCode, SsServiceStatus.dwServiceSpecificExitCode, SsServiceStatus.dwCheckPoint, SsServiceStatus.dwWaitHint )); } // // Call SetServiceStatus, ignoring any errors. // SetServiceStatus(SsServiceStatusHandle, &SsServiceStatus); } // AnnounceServiceStatus DWORD WINAPI ControlResponse( DWORD opCode, DWORD fdwEventType, LPVOID lpEventData, LPVOID lpContext ) { NET_API_STATUS error; USHORT i; BOOL announce = TRUE; // // Determine the type of service control message and modify the // service status, if necessary. // switch( opCode ) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: IF_DEBUG(CONTROL_MESSAGES) { SS_PRINT(( "ControlResponse: STOP control received.\n" )); } // // Announce that we are in the process of stopping. // SsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; AnnounceServiceStatus( 0 ); // // Set the event that will wake up the scavenger thread. // That thread will wake up and kill the server. // if ( !SetEvent( SsData.SsTerminationEvent ) ) { IF_DEBUG(TERMINATION_ERRORS) { SS_PRINT(( "ControlResponse: SetEvent failed: %ld\n", GetLastError( ) )); } } // // Let the main thread announce when the stop is done. // announce = FALSE; break; case SERVICE_CONTROL_PAUSE: IF_DEBUG(CONTROL_MESSAGES) { SS_PRINT(( "ControlResponse: PAUSE control received.\n" )); } // // Announce that we are in the process of pausing. // SsServiceStatus.dwCurrentState = SERVICE_PAUSE_PENDING; AnnounceServiceStatus( 0 ); // // Send the request on to the server. // error = SsServerFsControl( FSCTL_SRV_PAUSE, NULL, NULL, 0L ); SS_ASSERT( error == NO_ERROR ); // // Announce that we're now paused. // SsServiceStatus.dwCurrentState = SERVICE_PAUSED; break; case SERVICE_CONTROL_CONTINUE: IF_DEBUG(CONTROL_MESSAGES) { SS_PRINT(( "ControlResponse: CONTINUE control received.\n" )); } // // Announce that continue is pending. // SsServiceStatus.dwCurrentState = SERVICE_CONTINUE_PENDING; AnnounceServiceStatus( 0 ); // // Send the request on to the server. // error = SsServerFsControl( FSCTL_SRV_CONTINUE, NULL, NULL, 0L ); SS_ASSERT( error == NO_ERROR ); // // Announce that we're active now. // SsServiceStatus.dwCurrentState = SERVICE_RUNNING; break; case SERVICE_CONTROL_INTERROGATE: IF_DEBUG(CONTROL_MESSAGES) { SS_PRINT(( "ControlResponse: INTERROGATE control received.\n" )); } break; default: IF_DEBUG(CONTROL_MESSAGES) { SS_PRINT(( "ControlResponse: unknown code received.\n" )); } return ERROR_CALL_NOT_IMPLEMENTED; break; } if ( announce ) { AnnounceServiceStatus( 0 ); } return NO_ERROR; } // ControlResponse