windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/wkssvc/server/wsmain.c

1774 lines
42 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 <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;
PSVCHOST_GLOBAL_DATA WsLmsvcsGlobalData;
REG_NOTIFY_INFO RegNotifyInfo = {0};
HANDLE TerminateWorkItem = NULL;
CRITICAL_SECTION WsWorkerCriticalSection;
BOOL WsIsTerminating=FALSE;
BOOL WsLUIDDeviceMapsEnabled=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
);
VOID
WsInitChangeNotify(
PVOID pData);
BOOL
WsReInitChangeNotify(
PREG_NOTIFY_INFO pNotifyInfo
);
DWORD
WsRegistryNotify(
LPVOID pParms,
BOOLEAN fWaitStatus
);
VOID
WsTerminationNotify(
LPVOID pParms,
BOOLEAN fWaitStatus
);
#ifdef WS_SET_TIME
STATIC
VOID
WsSetTime(
VOID
);
STATIC
DWORD
WsFindTimeServer(
LPTSTR *pServerName
);
STATIC
BOOL
WsShouldSetTime(
VOID
);
#endif
STATIC
BOOL
WsGetLUIDDeviceMapsEnabled(
VOID
);
VOID
SvchostPushServiceGlobals(
PSVCHOST_GLOBAL_DATA pGlobals
)
{
WsLmsvcsGlobalData = pGlobals;
}
VOID
ServiceMain(
DWORD NumArgs,
LPTSTR *ArgsArray
)
/*++
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);
//
// Make sure svchost.exe gave us the global data
//
ASSERT(WsLmsvcsGlobalData != NULL);
WsInitState = 0;
try {
InitializeCriticalSection(&WsWorkerCriticalSection);
} except( EXCEPTION_EXECUTE_HANDLER ) {
ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// Initialize the workstation.
//
if (WsInitializeWorkstation(&WsInitState) != NERR_Success) {
DbgPrint("WKSSVC failed to initialize workstation %x\n",WsInitState);
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;
}
ApiStatus = RtlRegisterWait(
&TerminateWorkItem, // work item handle
WsGlobalData.TerminateNowEvent, // wait handle
WsTerminationNotify, // callback fcn
NULL, // parameter
INFINITE, // timeout
WT_EXECUTEONLYONCE | // flags
WT_EXECUTELONGFUNCTION);
if (!NT_SUCCESS(ApiStatus)) {
ApiStatus = RtlNtStatusToDosError(ApiStatus);
goto Cleanup;
}
//
// Setup to monitor registry changes.
//
RegNotifyInfo.NotifyEventHandle = RegistryChangeEvent;
RegNotifyInfo.Timeout = INFINITE;
RegNotifyInfo.WorkItemHandle = NULL;
RegNotifyInfo.RegistryHandle = ConfigHandle;
EnterCriticalSection(&WsWorkerCriticalSection);
if (!WsReInitChangeNotify(&RegNotifyInfo)) {
ApiStatus = GetLastError();
RtlDeregisterWait(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:
DbgPrint("WKSSVC ServiceMain returned with %x\n",ApiStatus);
WsTerminationNotify(NULL, NO_ERROR);
return;
}
VOID
WsInitChangeNotify(
PVOID pData
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
UNREFERENCED_PARAMETER(pData);
RegNotifyChangeKeyValue (
ConfigHandle,
TRUE, // watch a subtree
REG_NOTIFY_CHANGE_LAST_SET,
RegistryChangeEvent,
TRUE // async call
);
return;
}
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;
NTSTATUS Status;
Status = RtlQueueWorkItem(
WsInitChangeNotify,
(PVOID)pNotifyInfo,
WT_EXECUTEONLYONCE);
if (!NT_SUCCESS(Status)) {
NetpKdPrint((PREFIX_WKSTA "Couldn't Initialize Registry Notify %d\n",
RtlNtStatusToDosError(Status)));
bStat = FALSE;
goto CleanExit;
}
//
// Add the work item that is to be called when the
// RegistryChangeEvent is signalled.
//
Status = RtlRegisterWait(
&pNotifyInfo->WorkItemHandle,
pNotifyInfo->NotifyEventHandle,
WsRegistryNotify,
(PVOID)pNotifyInfo,
pNotifyInfo->Timeout,
WT_EXECUTEONLYONCE | WT_EXECUTEINPERSISTENTIOTHREAD);
if (!NT_SUCCESS(Status)) {
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,
BOOLEAN fWaitStatus
)
/*++
Routine Description:
Handles Workstation Registry Notification. This function is called by a
thread pool 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(fWaitStatus);
//
// The NT thread pool requires explicit work item deregistration,
// even if we specified the WT_EXECUTEONLYONCE flag
//
RtlDeregisterWait(pNotifyinfo->WorkItemHandle);
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);
}
VOID
WsTerminationNotify(
LPVOID pParms,
BOOLEAN fWaitStatus
)
/*++
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(fWaitStatus);
IF_DEBUG(MAIN) {
NetpKdPrint((PREFIX_WKSTA "WORKSTATION_main: cleaning up, "
"api status.\n"));
}
//
// The NT thread pool requires explicit work item deregistration,
// even if we specified the WT_EXECUTEONLYONCE flag
//
if (TerminateWorkItem != NULL) {
RtlDeregisterWait(TerminateWorkItem);
}
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
);
WsIsTerminating = FALSE;
LeaveCriticalSection(&WsWorkerCriticalSection);
DeleteCriticalSection(&WsWorkerCriticalSection);
return;
}
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 the resource for serializing access to configuration
// information.
//
// This must be done before the redir is initialized
try {
RtlInitializeResource(&WsInfo.ConfigResource);
} except(EXCEPTION_EXECUTE_HANDLER) {
return RtlNtStatusToDosError(GetExceptionCode());
}
//
// Initialize workstation to receive service requests by registering the
// control handler.
//
if ((WsGlobalData.StatusHandle = RegisterServiceCtrlHandler(
SERVICE_WORKSTATION,
WkstaControlHandler
)) == (SERVICE_STATUS_HANDLE) 0) {
status = GetLastError();
WS_HANDLE_ERROR(WsErrorRegisterControlHandler);
DbgPrint("WKSSVC failed with RegisterServiceCtrlHandler %x\n",status);
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);
DbgPrint("WKSSVC failed with WsUpdateStatus %x\n",status);
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);
DbgPrint("WKSSVC failed with WsInitializeLsa %x\n",status);
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);
DbgPrint("WKSSVC failed with WsInitializeRedirector %x\n",status);
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);
DbgPrint("WKSSVC failed with WsBindToTransports %x\n",status);
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);
DbgPrint("WKSSVC failed with WsAddDomains %x\n",status);
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);
DbgPrint("WKSSVC failed with WsCreateApiStructures %x\n",status);
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(
WORKSTATION_INTERFACE_NAME,
wkssvc_ServerIfHandle
)) != NERR_Success) {
WS_HANDLE_ERROR(WsErrorStartRpcServer);
DbgPrint("WKSSVC failed with StartRpcServer %x\n",status);
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);
DbgPrint("WKSSVC failed with WsInitializeDfs %x\n",status);
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);
DbgPrint("WKSSVC failed with WsUpdateStatus %x\n",status);
return status;
}
#ifdef WS_SET_TIME
//
// Set the Time
//
WsSetTime();
#endif
IF_DEBUG(MAIN) {
NetpKdPrint(("[Wksta] Successful Initialization\n"));
}
WsLUIDDeviceMapsEnabled = WsGetLUIDDeviceMapsEnabled();
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();
}
//
// Delete resource for serializing access to config information
// This must be done only after the redir is shutdown
// We do this here, (the Init is done in WsInitializeWorkstation routine above)
// to avoid putting additional synchronization on delete
// Otherwise we get into the situation where, the redir is shutting down and
// it deletes the resource while someone has acquired it, causing bad things.
RtlDeleteResource(&WsInfo.ConfigResource);
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) 0) {
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_SHUTDOWN:
//
// Lack of break is intentional!
//
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_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);
}
//
// NB The Set Time feature is currently disabled
//
return(FALSE);
// return(setTimeFlag);
}
#endif
STATIC
BOOL
WsGetLUIDDeviceMapsEnabled(
VOID
)
/*++
Routine Description:
This function calls NtQueryInformationProcess() to determine if
LUID device maps are enabled
Arguments:
none
Return Value:
TRUE - LUID device maps are enabled
FALSE - LUID device maps are disabled
--*/
{
NTSTATUS Status;
ULONG LUIDDeviceMapsEnabled;
BOOL Result;
Status = NtQueryInformationProcess( NtCurrentProcess(),
ProcessLUIDDeviceMapsEnabled,
&LUIDDeviceMapsEnabled,
sizeof(LUIDDeviceMapsEnabled),
NULL
);
if (!NT_SUCCESS( Status )) {
IF_DEBUG(MAIN) {
NetpKdPrint(("[Wksta] NtQueryInformationProcess(WsLUIDDeviceMapsEnabled) failed "
"FORMAT_API_STATUS" "\n",Status));
}
Result = FALSE;
}
else {
Result = (LUIDDeviceMapsEnabled != 0);
}
return( Result );
}