1774 lines
42 KiB
C
1774 lines
42 KiB
C
|
/*++
|
|||
|
|
|||
|
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 );
|
|||
|
}
|
|||
|
|
|||
|
|