/*++ Copyright (c) 1991-1992 Microsoft Corporation Module Name: wsmain.c Abstract: This is the main routine for the NT LAN Manager Workstation service. Author: Rita Wong (ritaw) 06-May-1991 Environment: User Mode - Win32 Revision History: 15-May-1992 JohnRo Implement registry watch. 11-Jun-1992 JohnRo Ifdef-out winreg notify stuff until we can fix logoff problem. Added assertion checks on registry watch stuff. 18-Oct-1993 terryk Removed WsInitializeLogon stuff 20-Oct-1993 terryk Remove WsInitializeMessage stuff --*/ #include "wsutil.h" // Common routines and data #include "wssec.h" // WkstaObjects create & destroy #include "wsdevice.h" // Device init & shutdown #include "wsuse.h" // UseStructures create & destroy #include "wsconfig.h" // Configuration loading #include "wslsa.h" // Lsa initialization #include "wsmsg.h" // Message send initialization #include "wswksta.h" // WsUpdateRedirToMatchWksta #include "wsmain.h" // Service related global definitions #include "wsdfs.h" // Dfs related routines #include // SV_TYPE_WORKSTATION #include // I_ScSetServiceBits #include // LMSVCS_ENTRY_POINT, LMSVCS_GLOBAL_DATA #include // Need NET_CONFIG_HANDLE typedef #include // NetpAllocConfigName(). #include // PREFIX_ equates. #ifdef WS_SET_TIME #include // NetRemoteTOD #endif //-------------------------------------------------------------------// // // // String Definitions // // // //-------------------------------------------------------------------// #ifdef WS_SET_TIME #define CURRENT_CTRL_SET TEXT("system\\CurrentControlSet") #define WKSTA_KEY TEXT("Services\\LanmanWorkstation\\Parameters") #define SET_TIME_VALUE_NAME TEXT("SetTime") #endif //-------------------------------------------------------------------// // // // Structures // // //-------------------------------------------------------------------// typedef struct _REG_NOTIFY_INFO { HANDLE NotifyEventHandle; DWORD Timeout; HANDLE WorkItemHandle; HANDLE RegistryHandle; } REG_NOTIFY_INFO, *PREG_NOTIFY_INFO, *LPREG_NOTIFY_INFO; //-------------------------------------------------------------------// // // // Global variables // // // //-------------------------------------------------------------------// WS_GLOBAL_DATA WsGlobalData; PLMSVCS_GLOBAL_DATA WsLmsvcsGlobalData; HANDLE WsDllRefHandle; REG_NOTIFY_INFO RegNotifyInfo = {0}; HANDLE TerminateWorkItem = NULL; CRITICAL_SECTION WsWorkerCriticalSection; BOOL WsIsTerminating=FALSE; DWORD WsNumWorkerThreads=0; // Used by the termination routine: BOOL ConfigHandleOpened = FALSE; HKEY ConfigHandle; HANDLE RegistryChangeEvent = NULL; LPTSTR RegPathToWatch = NULL; DWORD WsInitState = 0; //-------------------------------------------------------------------// // // // Function prototypes // // // //-------------------------------------------------------------------// STATIC NET_API_STATUS WsInitializeWorkstation( OUT LPDWORD WsInitState ); STATIC VOID WsShutdownWorkstation( IN NET_API_STATUS ErrorCode, IN DWORD WsInitState ); STATIC VOID WsHandleError( IN WS_ERROR_CONDITION FailingCondition, IN NET_API_STATUS Status, IN DWORD WsInitState ); STATIC NET_API_STATUS WsCreateApiStructures( IN OUT LPDWORD WsInitState ); STATIC VOID WsDestroyApiStructures( IN DWORD WsInitState ); VOID WkstaControlHandler( IN DWORD Opcode ); DWORD WsInitChangeNotify( PVOID pData, DWORD dwWaitStatus ); BOOL WsReInitChangeNotify( PREG_NOTIFY_INFO pNotifyInfo ); DWORD WsRegistryNotify( LPVOID pParms, DWORD dwWaitStatus ); DWORD WsTerminationNotify( LPVOID pParms, DWORD dwWaitStatus ); #ifdef WS_SET_TIME STATIC VOID WsSetTime( VOID ); STATIC DWORD WsFindTimeServer( LPTSTR *pServerName ); STATIC BOOL WsShouldSetTime( VOID ); #endif VOID LMSVCS_ENTRY_POINT( // (WORKSTATION_main) DWORD NumArgs, LPTSTR *ArgsArray, PLMSVCS_GLOBAL_DATA LmsvcsGlobalData, HANDLE SvcRefHandle ) /*++ Routine Description: This is the main routine of the Workstation Service which registers itself as an RPC server and notifies the Service Controller of the Workstation service control entry point. After the workstation is started, this thread is used (since it's otherwise unused) to watch for changes to the registry. Arguments: NumArgs - Supplies the number of strings specified in ArgsArray. ArgsArray - Supplies string arguments that are specified in the StartService API call. This parameter is ignored by the Workstation service. Return Value: None. --*/ { NET_API_STATUS ApiStatus; UNREFERENCED_PARAMETER(NumArgs); UNREFERENCED_PARAMETER(ArgsArray); WsDllRefHandle = SvcRefHandle; // // Save the LMSVCS global data for future use. // WsLmsvcsGlobalData = LmsvcsGlobalData; InitializeCriticalSection(&WsWorkerCriticalSection); // // Initialize the workstation. // if (WsInitializeWorkstation(&WsInitState) != NERR_Success) { return; } // // Set up to wait for registry change or terminate event. // ApiStatus = NetpAllocConfigName( SERVICES_ACTIVE_DATABASE, SERVICE_WORKSTATION, NULL, // default area ("Parameters") &RegPathToWatch ); if (ApiStatus != NERR_Success) { goto Cleanup; } NetpAssert(RegPathToWatch != NULL && *RegPathToWatch != TCHAR_EOS); ApiStatus = (NET_API_STATUS) RegOpenKeyEx( HKEY_LOCAL_MACHINE, // hKey RegPathToWatch, // lpSubKey 0L, // ulOptions (reserved) KEY_READ | KEY_NOTIFY, // desired access &ConfigHandle // Newly Opened Key Handle ); if (ApiStatus != NO_ERROR) { goto Cleanup; } ConfigHandleOpened = TRUE; RegistryChangeEvent = CreateEvent( NULL, // no security descriptor FALSE, // use automatic reset FALSE, // initial state: not signalled NULL // no name ); if (RegistryChangeEvent == NULL) { ApiStatus = (NET_API_STATUS) GetLastError(); goto Cleanup; } TerminateWorkItem = WsLmsvcsGlobalData->SvcsAddWorkItem ( WsGlobalData.TerminateNowEvent, // wait handle WsTerminationNotify, // callback fcn NULL, // parameter SVC_QUEUE_WORK_ITEM, // flags INFINITE, // timeout WsDllRefHandle); // dll ref handle // // Setup to monitor registry changes. // RegNotifyInfo.NotifyEventHandle = RegistryChangeEvent; RegNotifyInfo.Timeout = INFINITE; RegNotifyInfo.WorkItemHandle = NULL; RegNotifyInfo.RegistryHandle = ConfigHandle; EnterCriticalSection(&WsWorkerCriticalSection); if (!WsReInitChangeNotify(&RegNotifyInfo)) { ApiStatus = GetLastError(); WsLmsvcsGlobalData->SvcsRemoveWorkItem(TerminateWorkItem); LeaveCriticalSection(&WsWorkerCriticalSection); goto Cleanup; } LeaveCriticalSection(&WsWorkerCriticalSection); // // This thread has done all that it can do. So we can return it // to the service controller. // return; Cleanup: WsTerminationNotify(NULL, NO_ERROR); return; } DWORD WsInitChangeNotify( PVOID pData, DWORD dwWaitStatus ) /*++ Routine Description: Arguments: Return Value: --*/ { UNREFERENCED_PARAMETER(pData); UNREFERENCED_PARAMETER(dwWaitStatus); return((NET_API_STATUS) RegNotifyChangeKeyValue ( ConfigHandle, TRUE, // watch a subtree REG_NOTIFY_CHANGE_LAST_SET, RegistryChangeEvent, TRUE // async call )); } BOOL WsReInitChangeNotify( PREG_NOTIFY_INFO pNotifyInfo ) /*++ Routine Description: NOTE: This function should only be called when in the WsWorkerCriticalSection. Arguments: Return Value: --*/ { BOOL bStat = TRUE; pNotifyInfo->WorkItemHandle = WsLmsvcsGlobalData->SvcsAddWorkItem( NULL, WsInitChangeNotify, (PVOID)pNotifyInfo, SVC_IMMEDIATE_CALLBACK, INFINITE, NULL); if (pNotifyInfo->WorkItemHandle == NULL) { NetpKdPrint((PREFIX_WKSTA "Couldn't Initialize Registry Notify %d\n",GetLastError())); bStat = FALSE; goto CleanExit; } // // Add the work item that is to be called when the // RegistryChangeEvent is signalled. // pNotifyInfo->WorkItemHandle = WsLmsvcsGlobalData->SvcsAddWorkItem( pNotifyInfo->NotifyEventHandle, WsRegistryNotify, (PVOID)pNotifyInfo, SVC_QUEUE_WORK_ITEM, pNotifyInfo->Timeout, WsDllRefHandle); if (pNotifyInfo->WorkItemHandle == NULL) { NetpKdPrint((PREFIX_WKSTA "Couldn't add Reg Notify work item\n")); bStat = FALSE; } CleanExit: if (bStat) { if (WsNumWorkerThreads == 0) { WsNumWorkerThreads++; } } else { if (WsNumWorkerThreads == 1) { WsNumWorkerThreads--; } } return(bStat); } DWORD WsRegistryNotify( LPVOID pParms, DWORD dwWaitStatus ) /*++ Routine Description: Handles Workstation Registry Notification. This function is called by a Services Worker thread when the event used for registry notification is signaled. Arguments: Return Value: --*/ { NET_API_STATUS ApiStatus; PREG_NOTIFY_INFO pNotifyinfo=(PREG_NOTIFY_INFO)pParms; NET_CONFIG_HANDLE NetConfigHandle; UNREFERENCED_PARAMETER(dwWaitStatus); EnterCriticalSection(&WsWorkerCriticalSection); if (WsIsTerminating) { WsNumWorkerThreads--; SetEvent(WsGlobalData.TerminateNowEvent); LeaveCriticalSection(&WsWorkerCriticalSection); return(NO_ERROR); } // // Serialize write access to config information // if (RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) { // // Update the redir fields based on change notify. // WsUpdateWkstaToMatchRegistry expects a NET_CONFIG_HANDLE // handle, so we conjure up one from the HKEY handle. // NetConfigHandle.WinRegKey = ConfigHandle; WsUpdateWkstaToMatchRegistry(&NetConfigHandle, FALSE); ApiStatus = WsUpdateRedirToMatchWksta( PARMNUM_ALL, NULL ); NetpAssert( ApiStatus == NO_ERROR ); RtlReleaseResource(&WsInfo.ConfigResource); } if (!WsReInitChangeNotify(&RegNotifyInfo)) { // // If we can't add the work item, then we just won't // listen for registry changes. There's not a whole // lot we can do here. // ApiStatus = GetLastError(); } LeaveCriticalSection(&WsWorkerCriticalSection); return(NO_ERROR); } DWORD WsTerminationNotify( LPVOID pParms, DWORD dwWaitStatus ) /*++ Routine Description: This function gets called by a services worker thread when the termination event gets signaled. Arguments: Return Value: --*/ { UNREFERENCED_PARAMETER(pParms); UNREFERENCED_PARAMETER(dwWaitStatus); IF_DEBUG(MAIN) { NetpKdPrint((PREFIX_WKSTA "WORKSTATION_main: cleaning up, " "api status.\n")); } EnterCriticalSection(&WsWorkerCriticalSection); WsIsTerminating = TRUE; // // Must close winreg handle (which turns off notify) before event handle. // Closing the regkey handle generates a change notify event! // if (ConfigHandleOpened) { (VOID) RegCloseKey(ConfigHandle); #if DBG // // Workaround for a benign winreg assertion caused by us // closing the RegistryChangeEvent handle which it wants // to signal. // Sleep(2000); #endif } if (RegPathToWatch != NULL) { (VOID) NetApiBufferFree(RegPathToWatch); } if ((RegistryChangeEvent != NULL) && (WsNumWorkerThreads != 0)) { // // There is still a RegistryNotify Work Item in the system, we // will attempt to remove it by setting the event to wake it up. // ResetEvent(WsGlobalData.TerminateNowEvent); LeaveCriticalSection(&WsWorkerCriticalSection); SetEvent(RegistryChangeEvent); // // Wait until the WsRegistryNotify Thread is finished. // We will give it 60 seconds. If the thread isn't // finished in that time frame, we will go on anyway. // WaitForSingleObject( WsGlobalData.TerminateNowEvent, 60000); if (WsNumWorkerThreads != 0) { NetpKdPrint((PREFIX_WKSTA "WsTerminationNotify: " "Registry Notification thread didn't terminate\n")); } EnterCriticalSection(&WsWorkerCriticalSection); } (VOID) CloseHandle(RegistryChangeEvent); // // Shutting down // // NOTE: We must synchronize with the RegistryNotification Thread. // WsShutdownWorkstation( NERR_Success, WsInitState ); return(NO_ERROR); } STATIC NET_API_STATUS WsInitializeWorkstation( OUT LPDWORD WsInitState ) /*++ Routine Description: This function initializes the Workstation service. Arguments: WsInitState - Returns a flag to indicate how far we got with initializing the Workstation service before an error occured. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS status; // // Initialize all the status fields so that subsequent calls to // SetServiceStatus need to only update fields that changed. // WsGlobalData.Status.dwServiceType = SERVICE_WIN32; WsGlobalData.Status.dwCurrentState = SERVICE_START_PENDING; WsGlobalData.Status.dwControlsAccepted = 0; WsGlobalData.Status.dwCheckPoint = 1; WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME; SET_SERVICE_EXITCODE( NO_ERROR, WsGlobalData.Status.dwWin32ExitCode, WsGlobalData.Status.dwServiceSpecificExitCode ); // // Initialize workstation to receive service requests by registering the // control handler. // if ((WsGlobalData.StatusHandle = RegisterServiceCtrlHandler( SERVICE_WORKSTATION, WkstaControlHandler )) == (SERVICE_STATUS_HANDLE) NULL) { status = GetLastError(); WS_HANDLE_ERROR(WsErrorRegisterControlHandler); return status; } // // Create an event which is used by the service control handler to notify // the Workstation service that it is time to terminate. // if ((WsGlobalData.TerminateNowEvent = CreateEvent( NULL, // Event attributes TRUE, // Event must be manually reset FALSE, NULL // Initial state not signalled )) == NULL) { status = GetLastError(); WS_HANDLE_ERROR(WsErrorCreateTerminateEvent); return status; } (*WsInitState) |= WS_TERMINATE_EVENT_CREATED; // // Notify the Service Controller for the first time that we are alive // and we are start pending // if ((status = WsUpdateStatus()) != NERR_Success) { WS_HANDLE_ERROR(WsErrorNotifyServiceController); return status; } // // Initialize the workstation as a logon process with LSA, and // get the MS V 1.0 authentication package ID. // if ((status = WsInitializeLsa()) != NERR_Success) { WS_HANDLE_ERROR(WsErrorInitLsa); return status; } (*WsInitState) |= WS_LSA_INITIALIZED; // // Read the configuration information to initialize the redirector and // datagram receiver // if ((status = WsInitializeRedirector()) != NERR_Success) { WS_HANDLE_ERROR(WsErrorStartRedirector); return status; } (*WsInitState) |= WS_DEVICES_INITIALIZED; // // Service install still pending. Update checkpoint counter and the // status with the Service Controller. // (WsGlobalData.Status.dwCheckPoint)++; (void) WsUpdateStatus(); // // Bind to transports // if ((status = WsBindToTransports()) != NERR_Success) { WS_HANDLE_ERROR(WsErrorBindTransport); return status; } // // Service install still pending. Update checkpoint counter and the // status with the Service Controller. // (WsGlobalData.Status.dwCheckPoint)++; (void) WsUpdateStatus(); // // Add domain names. // if ((status = WsAddDomains()) != NERR_Success) { WS_HANDLE_ERROR(WsErrorAddDomains); return status; } // // Service start still pending. Update checkpoint counter and the // status with the Service Controller. // (WsGlobalData.Status.dwCheckPoint)++; (void) WsUpdateStatus(); // // Create Workstation service API data structures // if ((status = WsCreateApiStructures(WsInitState)) != NERR_Success) { WS_HANDLE_ERROR(WsErrorCreateApiStructures); return status; } // // Initialize the workstation service to receive RPC requests // // NOTE: Now all RPC servers in services.exe share the same pipe name. // However, in order to support communication with version 1.0 of WinNt, // it is necessary for the Client Pipe name to remain the same as // it was in version 1.0. Mapping to the new name is performed in // the Named Pipe File System code. // if ((status = WsLmsvcsGlobalData->StartRpcServer( WsLmsvcsGlobalData->SvcsRpcPipeName, wkssvc_ServerIfHandle )) != NERR_Success) { WS_HANDLE_ERROR(WsErrorStartRpcServer); return status; } (*WsInitState) |= WS_RPC_SERVER_STARTED; // // Lastly, we create a thread to communicate with the // Dfs-enabled MUP driver. // if ((status = WsInitializeDfs()) != NERR_Success) { WS_HANDLE_ERROR(WsErrorStartRedirector); return status; } (*WsInitState) |= WS_DFS_THREAD_STARTED; (void) I_ScSetServiceBits( WsGlobalData.StatusHandle, SV_TYPE_WORKSTATION, TRUE, TRUE, NULL ); // // We are done with starting the Workstation service. Tell Service // Controller our new status. // WsGlobalData.Status.dwCurrentState = SERVICE_RUNNING; WsGlobalData.Status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN; WsGlobalData.Status.dwCheckPoint = 0; WsGlobalData.Status.dwWaitHint = 0; if ((status = WsUpdateStatus()) != NERR_Success) { WS_HANDLE_ERROR(WsErrorNotifyServiceController); return status; } #ifdef WS_SET_TIME // // Set the Time // WsSetTime(); #endif IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] Successful Initialization\n")); } return NERR_Success; } VOID WsShutdownWorkstation( IN NET_API_STATUS ErrorCode, IN DWORD WsInitState ) /*++ Routine Description: This function shuts down the Workstation service. Arguments: ErrorCode - Supplies the error code of the failure WsInitState - Supplies a flag to indicate how far we got with initializing the Workstation service before an error occured, thus the amount of clean up needed. Return Value: None. --*/ { NET_API_STATUS status = NERR_Success; // // Service stop still pending. Update checkpoint counter and the // status with the Service Controller. // (WsGlobalData.Status.dwCheckPoint)++; (void) WsUpdateStatus(); if (WsInitState & WS_DFS_THREAD_STARTED) { // // Stop the Dfs thread // WsShutdownDfs(); } if (WsInitState & WS_RPC_SERVER_STARTED) { // // Stop the RPC server // WsLmsvcsGlobalData->StopRpcServer(wkssvc_ServerIfHandle); } if (WsInitState & WS_API_STRUCTURES_CREATED) { // // Destroy data structures created for Workstation APIs // WsDestroyApiStructures(WsInitState); } WsShutdownMessageSend(); // // Don't need to ask redirector to unbind from its transports when // cleaning up because the redirector will tear down the bindings when // it stops. // if (WsInitState & WS_DEVICES_INITIALIZED) { // // Shut down the redirector and datagram receiver // status = WsShutdownRedirector(); } if (WsInitState & WS_LSA_INITIALIZED) { // // Deregister workstation as logon process // WsShutdownLsa(); } if (WsInitState & WS_TERMINATE_EVENT_CREATED) { // // Close handle to termination event // CloseHandle(WsGlobalData.TerminateNowEvent); } I_ScSetServiceBits( WsGlobalData.StatusHandle, SV_TYPE_WORKSTATION, FALSE, TRUE, NULL ); // // We are done with cleaning up. Tell Service Controller that we are // stopped. // WsGlobalData.Status.dwCurrentState = SERVICE_STOPPED; WsGlobalData.Status.dwControlsAccepted = 0; if ((ErrorCode == NERR_Success) && (status == ERROR_REDIRECTOR_HAS_OPEN_HANDLES)) { ErrorCode = status; } SET_SERVICE_EXITCODE( ErrorCode, WsGlobalData.Status.dwWin32ExitCode, WsGlobalData.Status.dwServiceSpecificExitCode ); WsGlobalData.Status.dwCheckPoint = 0; WsGlobalData.Status.dwWaitHint = 0; (void) WsUpdateStatus(); } STATIC VOID WsHandleError( IN WS_ERROR_CONDITION FailingCondition, IN NET_API_STATUS Status, IN DWORD WsInitState ) /*++ Routine Description: This function handles a Workstation service error condition. If the error condition is fatal, it shuts down the Workstation service. Arguments: FailingCondition - Supplies a value which indicates what the failure is. Status - Supplies the status code for the failure. WsInitState - Supplies a flag to indicate how far we got with initializing the Workstation service before an error occured, thus the amount of clean up needed. Return Value: None. --*/ { switch (FailingCondition) { case WsErrorRegisterControlHandler: NetpKdPrint(("Workstation cannot register control handler " FORMAT_API_STATUS "\n", Status)); WS_SHUTDOWN_WORKSTATION(Status); break; case WsErrorCreateTerminateEvent: NetpKdPrint(("[Wksta] Cannot create done event " FORMAT_API_STATUS "\n", Status)); WS_SHUTDOWN_WORKSTATION(Status); break; case WsErrorNotifyServiceController: NetpKdPrint(("[Wksta] SetServiceStatus error " FORMAT_API_STATUS "\n", Status)); WS_SHUTDOWN_WORKSTATION(Status); break; case WsErrorInitLsa: NetpKdPrint(("[Wksta] LSA initialization error " FORMAT_API_STATUS "\n", Status)); WS_SHUTDOWN_WORKSTATION(Status); break; case WsErrorStartRedirector: NetpKdPrint(("[Wksta] Cannot start redirector " FORMAT_API_STATUS "\n", Status)); WS_SHUTDOWN_WORKSTATION(Status); break; case WsErrorBindTransport: if (Status == NERR_ItemNotFound) { NetpKdPrint(("[Wksta] Did not bind to any transport driver\n")); } WS_SHUTDOWN_WORKSTATION(Status); break; case WsErrorAddDomains: NetpKdPrint(("[Wksta] Could not add domain names " FORMAT_API_STATUS "\n", Status)); WS_SHUTDOWN_WORKSTATION(Status); break; case WsErrorStartRpcServer: NetpKdPrint(("[Wksta] Cannot start RPC server " FORMAT_API_STATUS "\n", Status)); WS_SHUTDOWN_WORKSTATION(Status); break; case WsErrorCreateApiStructures: NetpKdPrint(("[Wksta] Error in creating API structures " FORMAT_API_STATUS "\n", Status)); WS_SHUTDOWN_WORKSTATION(Status); break; default: NetpKdPrint(("[Wksta] WsHandleError: unknown error condition %lu\n", FailingCondition)); NetpAssert(FALSE); } } NET_API_STATUS WsUpdateStatus( VOID ) /*++ Routine Description: This function updates the Workstation service status with the Service Controller. Arguments: None. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS status = NERR_Success; if (WsGlobalData.StatusHandle == (SERVICE_STATUS_HANDLE) NULL) { NetpKdPrint(( "[Wksta] Cannot call SetServiceStatus, no status handle.\n" )); return ERROR_INVALID_HANDLE; } if (! SetServiceStatus(WsGlobalData.StatusHandle, &WsGlobalData.Status)) { status = GetLastError(); IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] SetServiceStatus error %lu\n", status)); } } return status; } STATIC NET_API_STATUS WsCreateApiStructures( IN OUT LPDWORD WsInitState ) /*++ Routine Description: This function creates and initializes all the data structures required for the Workstation APIs. Arguments: WsInitState - Returns the supplied flag of how far we got in the Workstation service initialization process. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS status; // // Create workstation security objects // if ((status = WsCreateWkstaObjects()) != NERR_Success) { return status; } (*WsInitState) |= WS_SECURITY_OBJECTS_CREATED; // // Create Use Table // if ((status = WsInitUseStructures()) != NERR_Success) { return status; } (*WsInitState) |= WS_USE_TABLE_CREATED; return NERR_Success; } STATIC VOID WsDestroyApiStructures( IN DWORD WsInitState ) /*++ Routine Description: This function destroys the data structures created for the Workstation APIs. Arguments: WsInitState - Supplies a flag which tells us what API structures were created in the initialization process and now have to be cleaned up. Return Value: None. --*/ { if (WsInitState & WS_USE_TABLE_CREATED) { // // Destroy Use Table // WsDestroyUseStructures(); } if (WsInitState & WS_SECURITY_OBJECTS_CREATED) { // // Destroy workstation security objects // WsDestroyWkstaObjects(); } } VOID WkstaControlHandler( IN DWORD Opcode ) /*++ Routine Description: This is the service control handler of the Workstation service. Arguments: Opcode - Supplies a value which specifies the action for the Workstation service to perform. Arg - Supplies a value which tells a service specifically what to do for an operation specified by Opcode. Return Value: None. --*/ { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] In Control Handler\n")); } switch (Opcode) { case SERVICE_CONTROL_PAUSE: // // Pause redirection of print and comm devices // WsPauseOrContinueRedirection( PauseRedirection ); break; case SERVICE_CONTROL_CONTINUE: // // Resume redirection of print and comm devices // WsPauseOrContinueRedirection( ContinueRedirection ); break; case SERVICE_CONTROL_STOP: if (WsGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] Stopping workstation...\n")); } WsGlobalData.Status.dwCurrentState = SERVICE_STOP_PENDING; WsGlobalData.Status.dwCheckPoint = 1; WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME; // // Send the status response. // (void) WsUpdateStatus(); if (! SetEvent(WsGlobalData.TerminateNowEvent)) { // // Problem with setting event to terminate Workstation // service. // NetpKdPrint(("[Wksta] Error setting TerminateNowEvent " FORMAT_API_STATUS "\n", GetLastError())); NetpAssert(FALSE); } return; } break; case SERVICE_CONTROL_SHUTDOWN: { if (LoadedMRxSmbInsteadOfRdr) { NET_API_STATUS status; HKEY hRedirectorKey; DWORD FinalStatus; status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, MRXSMB_REGISTRY_KEY, 0, KEY_ALL_ACCESS, &hRedirectorKey); if (status == ERROR_SUCCESS) { DWORD ValueType; DWORD ValueSize; // if this is a controlled shutdown reload the new redirector on // restart if it is running currently. ValueSize = sizeof(FinalStatus); status = RegQueryValueEx( hRedirectorKey, LAST_LOAD_STATUS, NULL, &ValueType, (PCHAR)&FinalStatus, &ValueSize); // If the query value was successful and the value is the same // as the original value written during load time, ensure that // the registry is updated for loading the new RDR. if ((status == ERROR_SUCCESS) && (FinalStatus == ERROR_NOT_READY)) { FinalStatus = ERROR_SUCCESS; status = RegSetValueEx( hRedirectorKey, LAST_LOAD_STATUS, 0, REG_DWORD, (PCHAR)&FinalStatus, sizeof(DWORD)); } if (status == ERROR_SUCCESS) { NetpKdPrint((PREFIX_WKSTA "New RDR will be loaded on restart\n")); } RegCloseKey(hRedirectorKey); } } } break; case SERVICE_CONTROL_INTERROGATE: break; default: IF_DEBUG(MAIN) { NetpKdPrint(("Unknown workstation opcode " FORMAT_HEX_DWORD "\n", Opcode)); } } // // Send the status response. // (void) WsUpdateStatus(); } #ifdef WS_SET_TIME STATIC VOID WsSetTime( VOID ) /*++ Routine Description: This function sets the time on the local machine. Arguments: None. Return Value: None. --*/ { NTSTATUS ntStatus; NET_API_STATUS status; PTIME_OF_DAY_INFO pTod; LPTSTR pServerName; LARGE_INTEGER systemTime; LARGE_INTEGER previousTime; ULONG privileges[1]; // // Look in registry to see if we are to set the time. // if (!WsShouldSetTime()) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] Time update is NOT requested\n", status)); } return; } // // Find a server to get the time from. // status = WsFindTimeServer(&pServerName); if (status != NERR_Success) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] WsFindTimeServer Failed "FORMAT_API_STATUS" \n", status)); } return; } // // Get the time // status = NetRemoteTOD( pServerName, (LPBYTE *)pTod ); if (status != NERR_Success) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] NetRemoteTOD Failed "FORMAT_API_STATUS" \n", status)); } NetApiBufferFree(pServerName); return; } NetApiBufferFree(pServerName); // // Convert the time to NT time. // RtlSecondsSince1970ToTime( pTod->tod_elapsedt, // ULONG &systemTime // PLARGE_INTEGER ); // // Set the NT system time. (first get privilege) // privileges[0] = SE_SYSTEMTIME_PRIVILEGE; status = NetpGetPrivilege(1,privileges); if (status != NO_ERROR) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] NetpGetPrivilege Failed "FORMAT_DWORD" \n", status)); } NetApiBufferFree(pServerName); return; } ntStatus = NtSetSystemTime( &systemTime, // IN PLARGE_INTEGER &previousTime // OUT PLARGE_INTEGER ); (VOID)NetpReleasePrivilege(); if (!NT_SUCCESS(ntStatus)) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] NtSetSystemTime Failed "FORMAT_NTSTATUS" \n", ntStatus)); } } return; } STATIC DWORD WsFindTimeServer( LPTSTR *pServerName ) /*++ Routine Description: This function finds the server name for a TimeSource server. This function allocates storage for the name whose pointer is stored in pServerName. Arguments: pServerName - This is a pointer to a location where the pointer to the name of the TimeSource Server is to placed. Return Value: NERR_Success - If the operation was completely successful. assorted errors - if the operation failed in any way. --*/ #define SERVER_INFO_BUF_SIZE 512 { NET_API_STATUS status; DWORD entriesRead; DWORD totalEntries; DWORD resumeHandle; LPSERVER_INFO_100 pServerInfo = NULL; LPTSTR pDomainName; // // Get the name of our domain. // status = NetpGetDomainName(&pDomainName); if (status != NERR_Success) { return(status); } // // Get a short enumerated list of the timesource servers out there. // status = NetServerEnum ( NULL, // Local Server 100, (LPBYTE *)&pServerInfo, SERVER_INFO_BUF_SIZE, &entriesRead, &totalEntries, SV_TYPE_TIME_SOURCE, pDomainName, // PrimaryDomain &resumeHandle ); NetApiBufferFree(pDomainName); if (status != NERR_Success) { return(status); } if (entriesRead == 0) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta]FindTimeServer: No TimeSource Servers " "in Domain\n")); } if (pServerInfo != NULL) { NetApiBufferFree(pServerInfo); } return(ERROR_GEN_FAILURE); } // // Allocate storage and copy the Time Source Server name there // *pServerName = (LPTSTR) LocalAlloc( LMEM_ZEROINIT, (UINT) STRSIZE(pServerInfo->sv100_name) ); if (*pServerName == NULL) { NetApiBufferFree(pServerInfo); return(GetLastError()); } STRCPY(*pServerName, pServerInfo->sv100_name); NetApiBufferFree(pServerInfo); return(NERR_Success); } STATIC BOOL WsShouldSetTime( VOID ) /*++ Routine Description: This function looks in the registry to determine if the workstation service is to go out and find the time so it can set the time on this machine. Arguments: none Return Value: TRUE - The Workstation should set the time. FALSE - The Workstation should not set the time. --*/ { HKEY systemKey; HKEY wkstaKey; DWORD status; DWORD setTimeFlag; DWORD bufferSize; // // Get the key for the current system control set. // status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // hKey CURRENT_CTRL_SET, // lpSubKey 0L, // ulOptions (reserved) KEY_READ, // desired access &systemKey); // Newly Opened Key Handle if (status != NO_ERROR) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] RegOpenKeyEx (system key) failed " "FORMAT_API_STATUS" "\n",status)); } return (FALSE); } // // Get the Workstation Service Key // status = RegOpenKeyEx( systemKey, // hKey WKSTA_KEY, // lpSubKey 0L, // ulOptions (reserved) KEY_READ, // desired access &wkstaKey // Newly Opened Key Handle ); if (status != NO_ERROR) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] RegOpenKeyEx (wksta key) failed " "FORMAT_API_STATUS" "\n",status)); } RegCloseKey(systemKey); return (FALSE); } // // Read the SetTime Value. This is a DWORD sized object that is // expected to be non-zero if we are to read the time, or zero // if we are not. // bufferSize = sizeof(DWORD); status = RegQueryValueEx ( wkstaKey, // hKey SET_TIME_VALUE_NAME, // lpValueName NULL, // lpTitleIndex NULL, // lpType (LPBYTE)&setTimeFlag, // lpData &bufferSize // lpcbData ); RegCloseKey(systemKey); RegCloseKey(wkstaKey); if (status != NO_ERROR) { IF_DEBUG(MAIN) { NetpKdPrint(("[Wksta] RegQueryValueEx(SetTimeValue) failed " FORMAT_API_STATUS "\n",status)); } return(FALSE); } // // BUGBUG: The Set Time feature is currently disabled until I can // test it completely. I wanted to check in the code in order to // minimize merge problems. // return(FALSE); // return(setTimeFlag); } #endif