summaryrefslogtreecommitdiffstats
path: root/private/net/svcdlls/wkssvc/server/wsmain.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/net/svcdlls/wkssvc/server/wsmain.c')
-rw-r--r--private/net/svcdlls/wkssvc/server/wsmain.c1694
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
+