diff options
Diffstat (limited to 'private/net/svcdlls/wkssvc/server/wsmain.c')
-rw-r--r-- | private/net/svcdlls/wkssvc/server/wsmain.c | 1694 |
1 files changed, 1694 insertions, 0 deletions
diff --git a/private/net/svcdlls/wkssvc/server/wsmain.c b/private/net/svcdlls/wkssvc/server/wsmain.c new file mode 100644 index 000000000..12e9fce55 --- /dev/null +++ b/private/net/svcdlls/wkssvc/server/wsmain.c @@ -0,0 +1,1694 @@ +/*++ + +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 <lmserver.h> // SV_TYPE_WORKSTATION +#include <srvann.h> // I_ScSetServiceBits + +#include <services.h> // LMSVCS_ENTRY_POINT, LMSVCS_GLOBAL_DATA + +#include <configp.h> // Need NET_CONFIG_HANDLE typedef +#include <confname.h> // NetpAllocConfigName(). +#include <prefix.h> // PREFIX_ equates. + +#ifdef WS_SET_TIME +#include <lmremutl.h> // 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 + |