2027 lines
47 KiB
C
2027 lines
47 KiB
C
/*++
|
||
|
||
Copyright (c) 1992-1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
nwmain.c
|
||
|
||
Abstract:
|
||
|
||
Main module of the NetWare workstation service.
|
||
|
||
Author:
|
||
|
||
Rita Wong (ritaw) 11-Dec-1992
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
|
||
#include <nw.h>
|
||
#include <nwreg.h>
|
||
#include <wsipx.h>
|
||
#include <wsnwlink.h>
|
||
#include <nwmisc.h>
|
||
#include <winsta.h>
|
||
|
||
|
||
//
|
||
//
|
||
// GetProcAddr Prototype for winsta.dll function WinStationSetInformationW
|
||
//
|
||
|
||
typedef BOOLEAN (*PWINSTATION_SET_INFORMATION) (
|
||
HANDLE hServer,
|
||
ULONG SessionId,
|
||
WINSTATIONINFOCLASS WinStationInformationClass,
|
||
PVOID pWinStationInformation,
|
||
ULONG WinStationInformationLength
|
||
);
|
||
|
||
//
|
||
//
|
||
// GetProcAddr Prototype for winsta.dll function WinStationSendMessageW
|
||
//
|
||
|
||
typedef BOOLEAN
|
||
(*PWINSTATION_SEND_MESSAGE) (
|
||
HANDLE hServer,
|
||
ULONG LogonId,
|
||
LPWSTR pTitle,
|
||
ULONG TitleLength,
|
||
LPWSTR pMessage,
|
||
ULONG MessageLength,
|
||
ULONG Style,
|
||
ULONG Timeout,
|
||
PULONG pResponse,
|
||
BOOLEAN DoNotWait
|
||
);
|
||
//------------------------------------------------------------------
|
||
//
|
||
// Local Definitions
|
||
//
|
||
//------------------------------------------------------------------
|
||
|
||
#define NW_EVENT_MESSAGE_FILE L"nwevent.dll"
|
||
#define NW_MAX_POPUP_MESSAGE_LENGTH 512
|
||
|
||
#define REG_WORKSTATION_PROVIDER_PATH L"System\\CurrentControlSet\\Services\\NWCWorkstation\\networkprovider"
|
||
#define REG_PROVIDER_VALUE_NAME L"Name"
|
||
|
||
#define REG_WORKSTATION_PARAMETERS_PATH L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters"
|
||
#define REG_BURST_VALUE_NAME L"MaxBurstSize"
|
||
#define REG_GATEWAY_VALUE_NAME L"GatewayPrintOption"
|
||
#define REG_DISABLEPOPUP_VALUE_NAME L"DisablePopup"
|
||
#define REG_GW_NOCONNECT_VALUE_NAME L"GWDontConnectAtStart"
|
||
|
||
#define REG_SETUP_PATH L"System\\Setup"
|
||
#define REG_SETUP_VALUE_NAME L"SystemSetupInProgress"
|
||
|
||
//
|
||
// QFE release does not have this. so for QFE, we make it a no-op bit.
|
||
//
|
||
#ifdef QFE_BUILD
|
||
#define MB_SERVICE_NOTIFICATION 0
|
||
#endif
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Local Function Prototypes //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
DWORD
|
||
NwInitialize(
|
||
OUT LPDWORD NwInitState
|
||
);
|
||
|
||
DWORD
|
||
NwInitializeCritSects(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
NwInitializeWkstaInfo(
|
||
VOID
|
||
);
|
||
|
||
DWORD
|
||
NwInitializeMessage(
|
||
VOID
|
||
);
|
||
|
||
BOOL NwShutdownNotify(
|
||
DWORD dwCtrlType
|
||
);
|
||
|
||
VOID
|
||
NwShutdown(
|
||
IN DWORD ErrorCode,
|
||
IN DWORD NwInitState
|
||
);
|
||
|
||
VOID
|
||
NwShutdownMessage(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
NwControlHandler(
|
||
IN DWORD Opcode
|
||
);
|
||
|
||
DWORD
|
||
NwUpdateStatus(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
NwMessageThread(
|
||
IN HANDLE RdrHandle
|
||
);
|
||
|
||
VOID
|
||
NwDisplayMessage(
|
||
IN LUID LogonId,
|
||
IN LPWSTR Server,
|
||
IN LPWSTR Message
|
||
);
|
||
|
||
VOID
|
||
NwDisplayPopup(
|
||
IN LPNWWKS_POPUP_DATA lpPopupData
|
||
);
|
||
|
||
BOOL
|
||
SendMessageIfUserW(
|
||
LUID LogonId,
|
||
LPWSTR pMessage,
|
||
LPWSTR pTitle
|
||
);
|
||
|
||
BOOL
|
||
NwSetupInProgress(
|
||
VOID
|
||
);
|
||
|
||
BOOL
|
||
NwGetLUIDDeviceMapsEnabled(
|
||
VOID
|
||
);
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Global variables //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
//
|
||
// For service control
|
||
//
|
||
STATIC SERVICE_STATUS NwStatus;
|
||
STATIC SERVICE_STATUS_HANDLE NwStatusHandle = 0;
|
||
HANDLE NwDoneEvent = NULL ;
|
||
|
||
//
|
||
// For popping up errors.
|
||
//
|
||
HANDLE NwPopupEvent = NULL ;
|
||
HANDLE NwPopupDoneEvent = NULL ;
|
||
NWWKS_POPUP_DATA PopupData ;
|
||
|
||
//
|
||
// For NTAS vs Winnt
|
||
//
|
||
BOOL fIsWinnt = TRUE; // default is this is Workstation (FALSE = Server)
|
||
|
||
//
|
||
// Flag to control DBCS translations
|
||
//
|
||
|
||
extern LONG Japan = 0;
|
||
|
||
//
|
||
// Data global to nwsvc.exe
|
||
//
|
||
PSVCHOST_GLOBAL_DATA NwsvcGlobalData;
|
||
|
||
//
|
||
// Handle for receiving server messages
|
||
//
|
||
STATIC HANDLE NwRdrMessageHandle;
|
||
|
||
//
|
||
// Stores the network and print provider name
|
||
//
|
||
WCHAR NwProviderName[MAX_PATH] = L"";
|
||
|
||
// Stores the packet burst size
|
||
DWORD NwPacketBurstSize = 32 * 1024;
|
||
|
||
//
|
||
// remember if gateway is logged on
|
||
//
|
||
BOOL GatewayLoggedOn = FALSE ;
|
||
|
||
//
|
||
// should gateway always take up a connection?
|
||
//
|
||
BOOL GatewayConnectionAlways = TRUE ;
|
||
|
||
//
|
||
// critical sections used
|
||
//
|
||
CRITICAL_SECTION NwLoggedOnCritSec;
|
||
CRITICAL_SECTION NwPrintCritSec; // protect the linked list of printers
|
||
|
||
BOOL NwLUIDDeviceMapsEnabled;
|
||
|
||
//-------------------------------------------------------------------//
|
||
|
||
VOID
|
||
SvchostPushServiceGlobals(
|
||
PSVCHOST_GLOBAL_DATA pGlobals
|
||
)
|
||
{
|
||
NwsvcGlobalData = pGlobals;
|
||
}
|
||
|
||
|
||
VOID
|
||
ServiceMain(
|
||
DWORD NumArgs,
|
||
LPTSTR *ArgsArray
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the main entry point of the NetWare workstation service. After
|
||
the service has been initialized, this thread will wait on NwDoneEvent
|
||
for a signal to terminate the service.
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
DWORD NwInitState = 0;
|
||
|
||
|
||
UNREFERENCED_PARAMETER(NumArgs);
|
||
UNREFERENCED_PARAMETER(ArgsArray);
|
||
|
||
//
|
||
// Make sure svchost.exe gave us the global data
|
||
//
|
||
|
||
ASSERT(NwsvcGlobalData != NULL);
|
||
|
||
if (NwInitialize(&NwInitState) != NO_ERROR) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Wait until we are told to stop.
|
||
//
|
||
(void) WaitForSingleObject(
|
||
NwDoneEvent,
|
||
INFINITE
|
||
);
|
||
|
||
NwShutdown(
|
||
NO_ERROR, // Normal termination
|
||
NwInitState
|
||
);
|
||
}
|
||
|
||
|
||
DWORD
|
||
NwInitialize(
|
||
OUT LPDWORD NwInitState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes the NetWare workstation service.
|
||
|
||
Arguments:
|
||
|
||
NwInitState - Returns a flag to indicate how far we got with initializing
|
||
the service before an error occurred.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR or reason for failure.
|
||
|
||
Notes:
|
||
|
||
See IMPORTANT NOTE below.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
NT_PRODUCT_TYPE ProductType ;
|
||
LCID lcid;
|
||
|
||
//
|
||
// are we a winnt machine?
|
||
//
|
||
#ifdef GATEWAY_ENABLED
|
||
fIsWinnt = RtlGetNtProductType(&ProductType) ?
|
||
(ProductType == NtProductWinNt) :
|
||
FALSE ;
|
||
#else
|
||
fIsWinnt = TRUE;
|
||
#endif
|
||
|
||
//
|
||
// initialize all our critical sections as soon as we can
|
||
//
|
||
status = NwInitializeCritSects();
|
||
|
||
if (status != NO_ERROR)
|
||
{
|
||
KdPrint(("NWWORKSTATION: NwInitializeCritSects error %lu\n", status));
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Initialize all the status fields so that subsequent calls to
|
||
// SetServiceStatus need to only update fields that changed.
|
||
//
|
||
NwStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
||
NwStatus.dwCurrentState = SERVICE_START_PENDING;
|
||
NwStatus.dwControlsAccepted = 0;
|
||
NwStatus.dwCheckPoint = 1;
|
||
NwStatus.dwWaitHint = 5000;
|
||
NwStatus.dwWin32ExitCode = NO_ERROR;
|
||
NwStatus.dwServiceSpecificExitCode = 0;
|
||
|
||
//
|
||
// Initialize workstation to receive service requests by registering the
|
||
// control handler.
|
||
//
|
||
if ((NwStatusHandle = RegisterServiceCtrlHandlerW(
|
||
NW_WORKSTATION_SERVICE,
|
||
NwControlHandler
|
||
)) == 0) {
|
||
|
||
status = GetLastError();
|
||
KdPrint(("NWWORKSTATION: RegisterServiceCtrlHandlerW error %lu\n", status));
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Tell Service Controller that we are start pending.
|
||
//
|
||
(void) NwUpdateStatus();
|
||
|
||
//
|
||
// Don't run during GUI-mode setup (doing so can cause migration of
|
||
// registry keys the service opens to fail, deleting share names)
|
||
//
|
||
if (NwSetupInProgress())
|
||
{
|
||
//
|
||
// Fail silently so there's no Eventlog message to panic the user
|
||
//
|
||
NwShutdown(NO_ERROR, *NwInitState);
|
||
|
||
//
|
||
// Bit of a hack since ServiceMain will wait on the NwDoneEvent
|
||
// (which hasn't yet been created) if NwInitialize returns anything
|
||
// other than NO_ERROR. This error code isn't used for anything
|
||
// other than telling ServiceMain to return without waiting.
|
||
//
|
||
return ERROR_SERVICE_DISABLED;
|
||
}
|
||
|
||
//
|
||
// Create events to synchronize message popups
|
||
//
|
||
if (((NwPopupEvent = CreateEvent(
|
||
NULL, // no security descriptor
|
||
FALSE, // use automatic reset
|
||
FALSE, // initial state: not signalled
|
||
NULL // no name
|
||
)) == NULL)
|
||
|| ((NwPopupDoneEvent = CreateEvent(
|
||
NULL, // no security descriptor
|
||
FALSE, // use automatic reset
|
||
TRUE, // initial state: signalled
|
||
NULL // no name
|
||
)) == NULL))
|
||
{
|
||
status = GetLastError();
|
||
NwShutdown(status, *NwInitState);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Create event to synchronize termination
|
||
//
|
||
if ((NwDoneEvent = CreateEvent(
|
||
NULL, // no security descriptor
|
||
TRUE, // do not use automatic reset
|
||
FALSE, // initial state: not signalled
|
||
NULL // no name
|
||
)) == NULL) {
|
||
|
||
status = GetLastError();
|
||
NwShutdown(status, *NwInitState);
|
||
return status;
|
||
}
|
||
(*NwInitState) |= NW_EVENTS_CREATED;
|
||
|
||
|
||
//
|
||
// Load the redirector.
|
||
//
|
||
if ((status = NwInitializeRedirector()) != NO_ERROR) {
|
||
NwShutdown(status, *NwInitState);
|
||
return status;
|
||
}
|
||
(*NwInitState) |= NW_RDR_INITIALIZED;
|
||
|
||
//
|
||
// Service still start pending. Update checkpoint to reflect that
|
||
// we are making progress.
|
||
//
|
||
NwStatus.dwCheckPoint++;
|
||
(void) NwUpdateStatus();
|
||
|
||
//
|
||
// Bind to transports
|
||
//
|
||
status = NwBindToTransports();
|
||
|
||
//
|
||
// tommye MS 24187 / MCS 255
|
||
//
|
||
|
||
//
|
||
// G/CSNW has been unbound in the connection manager and so, we haven't
|
||
// found the linkage key to bind to.
|
||
//
|
||
|
||
if (status == ERROR_INVALID_PARAMETER) {
|
||
|
||
//
|
||
// Fail silently so there's no Eventlog message to panic the user
|
||
//
|
||
|
||
NwShutdown(NO_ERROR, *NwInitState);
|
||
|
||
//
|
||
// Bit of a hack since SvcEntry_NWCS will wait on the NwDoneEvent
|
||
// (which hasn't yet been created) if NwInitialize returns anything
|
||
// other than NO_ERROR. This error code isn't used for anything
|
||
// other than telling SvcEntry_NWCS to return without waiting.
|
||
//
|
||
|
||
return ERROR_SERVICE_DISABLED;
|
||
|
||
} else if (status != NO_ERROR) {
|
||
|
||
NwShutdown(status, *NwInitState);
|
||
return status;
|
||
}
|
||
(*NwInitState) |= NW_BOUND_TO_TRANSPORTS;
|
||
|
||
//
|
||
// Service still start pending. Update checkpoint to reflect that
|
||
// we are making progress.
|
||
//
|
||
NwStatus.dwCheckPoint++;
|
||
(void) NwUpdateStatus();
|
||
|
||
//
|
||
// Initialize credential management.
|
||
//
|
||
NwInitializeLogon();
|
||
|
||
//
|
||
// Setup thread to receive server messages. Even if not successful,
|
||
// just press on as the workstation is mostly functional.
|
||
//
|
||
if ((status = NwInitializeMessage()) == NO_ERROR) {
|
||
(*NwInitState) |= NW_INITIALIZED_MESSAGE;
|
||
}
|
||
|
||
//
|
||
// Service still start pending. Update checkpoint to reflect that
|
||
// we are making progress.
|
||
//
|
||
NwStatus.dwCheckPoint++;
|
||
(void) NwUpdateStatus();
|
||
|
||
//
|
||
// Read some workstation information stored in the registry
|
||
// and passes some info to the redirector. This has to be
|
||
// done before opening up the RPC interface.
|
||
//
|
||
NwInitializeWkstaInfo();
|
||
|
||
//
|
||
// Initialize the server side print provider.
|
||
//
|
||
NwInitializePrintProvider();
|
||
|
||
//
|
||
// Initialize the service provider.
|
||
//
|
||
NwInitializeServiceProvider();
|
||
|
||
//
|
||
// tommye - MS 176469
|
||
//
|
||
// No longer supporting the Gateway - so just force it to be disabled
|
||
//
|
||
|
||
#ifdef GATEWAY_ENABLED
|
||
|
||
//
|
||
// Login the Gateway account. if not setup, this is a no-op.
|
||
// Failures are non fatal.
|
||
//
|
||
if ((status = NwGatewayLogon()) == NO_ERROR) {
|
||
(*NwInitState) |= NW_GATEWAY_LOGON;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Service still start pending. Update checkpoint to reflect that
|
||
// we are making progress.
|
||
//
|
||
NwStatus.dwCheckPoint++;
|
||
(void) NwUpdateStatus();
|
||
|
||
//
|
||
// Open up the RPC interface
|
||
//
|
||
status = NwsvcGlobalData->StartRpcServer(
|
||
NWWKS_INTERFACE_NAME,
|
||
nwwks_ServerIfHandle
|
||
);
|
||
|
||
if (status != NO_ERROR) {
|
||
NwShutdown(status, *NwInitState);
|
||
return status;
|
||
}
|
||
(*NwInitState) |= NW_RPC_SERVER_STARTED;
|
||
|
||
//
|
||
// Set up the hook to handle computer shut down.
|
||
//
|
||
// IMPORTANT NOTE: this is the last step after everything else
|
||
// has suceeded. When shutdown handler is called, it assumes that
|
||
// the redir is fully initialized.
|
||
//
|
||
if ( !SetConsoleCtrlHandler( NwShutdownNotify, TRUE ))
|
||
{
|
||
KdPrint(("SetConsoleCtrlHandler failed with %d\n", GetLastError()));
|
||
NwShutdown( status, *NwInitState );
|
||
return GetLastError();
|
||
}
|
||
|
||
//
|
||
// We are done with workstation startup.
|
||
//
|
||
NwStatus.dwCurrentState = SERVICE_RUNNING;
|
||
NwStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
||
SERVICE_ACCEPT_SHUTDOWN;
|
||
NwStatus.dwCheckPoint = 0;
|
||
NwStatus.dwWaitHint = 0;
|
||
NwStatus.dwWin32ExitCode = NO_ERROR;
|
||
|
||
if ((status = NwUpdateStatus()) != NO_ERROR) {
|
||
NwShutdown(status, *NwInitState);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Read user and service logon credentias from the registry, in
|
||
// case user logged on before workstation was started.
|
||
// Eg. restart workstation.
|
||
//
|
||
NwGetLogonCredential();
|
||
|
||
|
||
#if 0
|
||
//
|
||
// check that the NWLINK has the right sockopts
|
||
//
|
||
// see comment on the actual function
|
||
//
|
||
if (!NwIsNWLinkVersionOK())
|
||
{
|
||
//
|
||
// log the error in the event log
|
||
//
|
||
|
||
LPWSTR InsertStrings[1] ;
|
||
|
||
NwLogEvent(EVENT_NWWKSTA_WRONG_NWLINK_VERSION,
|
||
0,
|
||
InsertStrings,
|
||
0) ;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Check to see if we're in a DBCS environment.
|
||
//
|
||
NtQueryDefaultLocale( TRUE, &lcid );
|
||
Japan = 0;
|
||
if (PRIMARYLANGID(lcid) == LANG_JAPANESE ||
|
||
PRIMARYLANGID(lcid) == LANG_KOREAN ||
|
||
PRIMARYLANGID(lcid) == LANG_CHINESE) {
|
||
|
||
Japan = 1;
|
||
}
|
||
|
||
NwLUIDDeviceMapsEnabled = NwGetLUIDDeviceMapsEnabled();
|
||
//
|
||
// Successful initialization
|
||
//
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
BOOL NwShutdownNotify(
|
||
IN DWORD dwCtrlType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is a control handler used in SetConsoleCtrlHandler.
|
||
We are only interested in CTRL_SHUTDOWN_EVENT. On shutdown, we
|
||
need to notify redirector to shut down and then delete the
|
||
CurrentUser key in the registry.
|
||
|
||
Arguments:
|
||
|
||
dwCtrlType - The control type that occurred. We will only
|
||
process CTRL_SHUTDOWN_EVENT.
|
||
|
||
Return Value:
|
||
|
||
TRUE if we don't want the default or other handlers to be called.
|
||
FALSE otherwise.
|
||
|
||
Note:
|
||
|
||
This Handler is registered after all the Init steps have completed.
|
||
As such, it does not check for what state the service is in as it
|
||
cleans up.
|
||
|
||
--*/
|
||
{
|
||
DWORD err;
|
||
|
||
#if DBG
|
||
IF_DEBUG(INIT)
|
||
KdPrint(("NwShutdownNotify\n"));
|
||
#endif
|
||
|
||
if ( dwCtrlType != CTRL_SHUTDOWN_EVENT )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// stop the RPC server
|
||
//
|
||
(void) NwsvcGlobalData->StopRpcServer(nwwks_ServerIfHandle);
|
||
|
||
//
|
||
// get rid of all connections
|
||
//
|
||
(void) DeleteAllConnections();
|
||
|
||
NwGatewayLogoff() ;
|
||
|
||
err = NwShutdownRedirector();
|
||
|
||
if ( err != NO_ERROR )
|
||
KdPrint(("Shut down redirector failed with %d\n", err ));
|
||
#if DBG
|
||
else
|
||
{
|
||
IF_DEBUG(INIT)
|
||
KdPrint(("NwShutdownRedirector success!\n"));
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Delete all logon session information in the registry.
|
||
//
|
||
NwDeleteInteractiveLogon(NULL);
|
||
|
||
(void) NwDeleteServiceLogon(NULL);
|
||
|
||
return FALSE; // The default handler will terminate the process.
|
||
}
|
||
|
||
|
||
VOID
|
||
NwShutdown(
|
||
IN DWORD ErrorCode,
|
||
IN DWORD NwInitState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function shuts down the Workstation service.
|
||
|
||
Arguments:
|
||
|
||
ErrorCode - Supplies the error code of the failure
|
||
|
||
NwInitState - Supplies a flag to indicate how far we got with initializing
|
||
the service before an error occurred, thus the amount of clean up
|
||
needed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
DWORD status = NO_ERROR;
|
||
|
||
//
|
||
// Service stop still pending. Update checkpoint counter and the
|
||
// status with the Service Controller.
|
||
//
|
||
(NwStatus.dwCheckPoint)++;
|
||
(void) NwUpdateStatus();
|
||
|
||
if (NwInitState & NW_RPC_SERVER_STARTED) {
|
||
NwsvcGlobalData->StopRpcServer(nwwks_ServerIfHandle);
|
||
}
|
||
|
||
if (NwInitState & NW_INITIALIZED_MESSAGE) {
|
||
NwShutdownMessage();
|
||
}
|
||
|
||
if (NwInitState & NW_GATEWAY_LOGON)
|
||
{
|
||
(void) NwDeleteRedirections() ;
|
||
}
|
||
|
||
//
|
||
// Service stop still pending. Update checkpoint counter and the
|
||
// status with the Service Controller.
|
||
//
|
||
(NwStatus.dwCheckPoint)++;
|
||
(void) NwUpdateStatus();
|
||
|
||
if (NwInitState & NW_BOUND_TO_TRANSPORTS) {
|
||
DeleteAllConnections();
|
||
NwGatewayLogoff() ;
|
||
}
|
||
|
||
//
|
||
// Clean up the service provider.
|
||
//
|
||
// NwTerminateServiceProvider(); NOT CALLED! This is done at DLL unload time already.
|
||
|
||
//
|
||
// Clean up the server side print provider
|
||
//
|
||
NwTerminatePrintProvider();
|
||
|
||
//
|
||
// Service stop still pending. Update checkpoint counter and the
|
||
// status with the Service Controller.
|
||
//
|
||
(NwStatus.dwCheckPoint)++;
|
||
(void) NwUpdateStatus();
|
||
|
||
if (NwInitState & NW_RDR_INITIALIZED) {
|
||
//
|
||
// Unload the redirector
|
||
//
|
||
status = NwShutdownRedirector();
|
||
}
|
||
|
||
if (NwInitState & NW_EVENTS_CREATED) {
|
||
//
|
||
// Close handle to termination event and popup event
|
||
//
|
||
if (NwDoneEvent) CloseHandle(NwDoneEvent);
|
||
if (NwPopupEvent) CloseHandle(NwPopupEvent);
|
||
if (NwPopupDoneEvent) CloseHandle(NwPopupDoneEvent);
|
||
}
|
||
|
||
//
|
||
// We are done with cleaning up. Tell Service Controller that we are
|
||
// stopped.
|
||
//
|
||
NwStatus.dwCurrentState = SERVICE_STOPPED;
|
||
NwStatus.dwControlsAccepted = 0;
|
||
|
||
if ((ErrorCode == NO_ERROR) &&
|
||
(status == ERROR_REDIRECTOR_HAS_OPEN_HANDLES)) {
|
||
ErrorCode = status;
|
||
}
|
||
|
||
//
|
||
// Deregister the control handler
|
||
//
|
||
(void) SetConsoleCtrlHandler( NwShutdownNotify, FALSE ) ;
|
||
|
||
NwStatus.dwWin32ExitCode = ErrorCode;
|
||
NwStatus.dwServiceSpecificExitCode = 0;
|
||
|
||
NwStatus.dwCheckPoint = 0;
|
||
NwStatus.dwWaitHint = 0;
|
||
|
||
(void) NwUpdateStatus();
|
||
}
|
||
|
||
|
||
VOID
|
||
NwControlHandler(
|
||
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
|
||
service to perform.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
switch (Opcode) {
|
||
|
||
case SERVICE_CONTROL_STOP:
|
||
case SERVICE_CONTROL_SHUTDOWN:
|
||
|
||
if ((NwStatus.dwCurrentState != SERVICE_STOP_PENDING) &&
|
||
(NwStatus.dwCurrentState != SERVICE_STOPPED)){
|
||
|
||
NwStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
NwStatus.dwCheckPoint = 1;
|
||
NwStatus.dwWaitHint = 60000;
|
||
|
||
//
|
||
// Send the status response.
|
||
//
|
||
(void) NwUpdateStatus();
|
||
|
||
if (! SetEvent(NwDoneEvent)) {
|
||
|
||
//
|
||
// Problem with setting event to terminate Workstation
|
||
// service.
|
||
//
|
||
KdPrint(("NWWORKSTATION: Error setting NwDoneEvent %lu\n",
|
||
GetLastError()));
|
||
|
||
ASSERT(FALSE);
|
||
}
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case SERVICE_CONTROL_INTERROGATE:
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// Send the status response.
|
||
//
|
||
(void) NwUpdateStatus();
|
||
}
|
||
|
||
|
||
DWORD
|
||
NwUpdateStatus(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function updates the workstation service status with the Service
|
||
Controller.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
Return code from SetServiceStatus.
|
||
|
||
--*/
|
||
{
|
||
DWORD status = NO_ERROR;
|
||
|
||
|
||
if (NwStatusHandle == 0) {
|
||
KdPrint(("NWWORKSTATION: Cannot call SetServiceStatus, no status handle.\n"));
|
||
return ERROR_INVALID_HANDLE;
|
||
}
|
||
|
||
if (! SetServiceStatus(NwStatusHandle, &NwStatus)) {
|
||
|
||
status = GetLastError();
|
||
|
||
KdPrint(("NWWORKSTATION: SetServiceStatus error %lu\n", status));
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
NwInitializeWkstaInfo(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function reads some workstation info, including the packet burst
|
||
size and the provider name. We will ignore all errors that occurred when
|
||
reading from the registry and use the default values instead.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
DWORD err;
|
||
HKEY hkey;
|
||
DWORD dwTemp;
|
||
DWORD dwSize = sizeof( dwTemp );
|
||
LPWSTR pszProviderName = NULL;
|
||
|
||
//
|
||
// Read the Network and Print Provider Name.
|
||
//
|
||
// Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
|
||
// \NWCWorkstation\networkprovider
|
||
//
|
||
err = RegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE,
|
||
REG_WORKSTATION_PROVIDER_PATH,
|
||
REG_OPTION_NON_VOLATILE, // options
|
||
KEY_READ, // desired access
|
||
&hkey
|
||
);
|
||
|
||
if ( !err )
|
||
{
|
||
//
|
||
// Read the network provider name
|
||
//
|
||
err = NwReadRegValue(
|
||
hkey,
|
||
REG_PROVIDER_VALUE_NAME,
|
||
&pszProviderName
|
||
);
|
||
|
||
if ( !err )
|
||
{
|
||
wcscpy( NwProviderName, pszProviderName );
|
||
(void) LocalFree( (HLOCAL) pszProviderName );
|
||
|
||
#if DBG
|
||
IF_DEBUG(INIT)
|
||
{
|
||
KdPrint(("\nNWWORKSTATION: Provider Name = %ws\n",
|
||
NwProviderName ));
|
||
}
|
||
#endif
|
||
}
|
||
|
||
RegCloseKey( hkey );
|
||
}
|
||
|
||
if ( err )
|
||
{
|
||
KdPrint(("Error %d when reading provider name.\n", err ));
|
||
}
|
||
|
||
|
||
//
|
||
// Read the Packet Burst Size
|
||
//
|
||
// Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
|
||
// \NWCWorkstation\Parameters
|
||
//
|
||
err = RegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE,
|
||
REG_WORKSTATION_PARAMETERS_PATH,
|
||
REG_OPTION_NON_VOLATILE, // options
|
||
KEY_READ, // desired access
|
||
&hkey
|
||
);
|
||
|
||
if ( !err )
|
||
{
|
||
err = RegQueryValueExW( hkey,
|
||
REG_BURST_VALUE_NAME,
|
||
NULL,
|
||
NULL,
|
||
(LPBYTE) &dwTemp,
|
||
&dwSize );
|
||
|
||
if ( !err )
|
||
{
|
||
NwPacketBurstSize = dwTemp;
|
||
|
||
#if DBG
|
||
IF_DEBUG(INIT)
|
||
{
|
||
KdPrint(("\nNWWORKSTATION: Packet Burst Size = %d\n",
|
||
NwPacketBurstSize ));
|
||
}
|
||
#endif
|
||
}
|
||
|
||
err = RegQueryValueExW( hkey,
|
||
REG_GATEWAY_VALUE_NAME,
|
||
NULL,
|
||
NULL,
|
||
(LPBYTE) &dwTemp,
|
||
&dwSize );
|
||
|
||
if ( !err )
|
||
{
|
||
NwGatewayPrintOption = dwTemp;
|
||
|
||
#if DBG
|
||
IF_DEBUG(INIT)
|
||
{
|
||
KdPrint(("\nNWWORKSTATION: Gateway Print Option = %d\n",
|
||
NwGatewayPrintOption ));
|
||
}
|
||
#endif
|
||
}
|
||
|
||
err = RegQueryValueExW( hkey,
|
||
REG_GW_NOCONNECT_VALUE_NAME,
|
||
NULL,
|
||
NULL,
|
||
(LPBYTE) &dwTemp,
|
||
&dwSize );
|
||
|
||
if ( !err )
|
||
{
|
||
if (dwTemp == 1)
|
||
GatewayConnectionAlways = FALSE ;
|
||
}
|
||
|
||
RegCloseKey( hkey );
|
||
}
|
||
|
||
//
|
||
// Passes the information to the redirector
|
||
//
|
||
(void) NwRdrSetInfo(
|
||
NW_PRINT_OPTION_DEFAULT,
|
||
NwPacketBurstSize,
|
||
NULL,
|
||
0,
|
||
NwProviderName,
|
||
((NwProviderName != NULL) ?
|
||
wcslen( NwProviderName) * sizeof( WCHAR ) : 0 )
|
||
);
|
||
|
||
}
|
||
|
||
|
||
|
||
DWORD
|
||
NwInitializeMessage(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine opens a handle to the redirector device to receive
|
||
server messages and creates a thread to wait for the incoming
|
||
messages.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
UNICODE_STRING RdrName;
|
||
|
||
HKEY hkey;
|
||
DWORD dwTemp;
|
||
DWORD dwSize = sizeof( dwTemp );
|
||
BOOL fDisablePopup = FALSE ;
|
||
|
||
HANDLE ThreadHandle;
|
||
DWORD ThreadId;
|
||
|
||
//
|
||
// Read the Disable Popup Flag. By default it is cleared.
|
||
// We only set to TRUE if we find the value.
|
||
//
|
||
// Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
|
||
// \NWCWorkstation\Parameters
|
||
//
|
||
status = RegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE,
|
||
REG_WORKSTATION_PARAMETERS_PATH,
|
||
REG_OPTION_NON_VOLATILE, // options
|
||
KEY_READ, // desired access
|
||
&hkey
|
||
);
|
||
|
||
if ( status == NO_ERROR )
|
||
{
|
||
status = RegQueryValueExW( hkey,
|
||
REG_DISABLEPOPUP_VALUE_NAME,
|
||
NULL,
|
||
NULL,
|
||
(LPBYTE) &dwTemp,
|
||
&dwSize );
|
||
|
||
if ( status == NO_ERROR )
|
||
{
|
||
fDisablePopup = (dwTemp == 1);
|
||
}
|
||
|
||
RegCloseKey( hkey );
|
||
}
|
||
|
||
if (fDisablePopup)
|
||
{
|
||
return NO_ERROR ;
|
||
}
|
||
|
||
RtlInitUnicodeString(&RdrName, DD_NWFS_DEVICE_NAME_U);
|
||
|
||
status = NwMapStatus(
|
||
NwCallNtOpenFile(
|
||
&NwRdrMessageHandle,
|
||
FILE_GENERIC_READ | SYNCHRONIZE,
|
||
&RdrName,
|
||
0 // Handle for async call
|
||
)
|
||
);
|
||
|
||
if (status != NO_ERROR) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Create the thread to wait for incoming messages
|
||
//
|
||
ThreadHandle = CreateThread(
|
||
NULL,
|
||
0,
|
||
(LPTHREAD_START_ROUTINE) NwMessageThread,
|
||
(LPVOID) NwRdrMessageHandle,
|
||
0,
|
||
&ThreadId
|
||
);
|
||
|
||
if (ThreadHandle == NULL) {
|
||
(void) NtClose(NwRdrMessageHandle);
|
||
return GetLastError();
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
VOID
|
||
NwShutdownMessage(
|
||
VOID
|
||
)
|
||
{
|
||
(void) NtClose(NwRdrMessageHandle);
|
||
}
|
||
|
||
|
||
VOID
|
||
NwMessageThread(
|
||
IN HANDLE RdrHandle
|
||
)
|
||
{
|
||
NTSTATUS getmsg_ntstatus;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
DWORD ReturnVal, NumEventsToWaitOn ;
|
||
HANDLE EventsToWaitOn[3];
|
||
|
||
//BYTE OutputBuffer[48 * sizeof(WCHAR) + 256 * sizeof(WCHAR)]; //Need more space for terminal server
|
||
BYTE OutputBuffer[ 2 * sizeof(ULONG) + 48 * sizeof(WCHAR) + 256 * sizeof(WCHAR)]; // Need space for UID to redirect message to correct user
|
||
|
||
PNWR_SERVER_MESSAGE ServerMessage = (PNWR_SERVER_MESSAGE) OutputBuffer;
|
||
BOOL DoFsctl = TRUE ;
|
||
NWWKS_POPUP_DATA LocalPopupData ;
|
||
|
||
|
||
EventsToWaitOn[0] = NwDoneEvent;
|
||
EventsToWaitOn[1] = NwPopupEvent;
|
||
EventsToWaitOn[2] = RdrHandle;
|
||
|
||
while (TRUE) {
|
||
if (DoFsctl)
|
||
{
|
||
getmsg_ntstatus = NtFsControlFile(
|
||
RdrHandle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_NWR_GET_MESSAGE,
|
||
NULL,
|
||
0,
|
||
OutputBuffer,
|
||
sizeof(OutputBuffer)
|
||
);
|
||
|
||
DoFsctl = FALSE ;
|
||
}
|
||
|
||
if (NT_SUCCESS(getmsg_ntstatus))
|
||
{
|
||
NumEventsToWaitOn = 3 ;
|
||
}
|
||
else
|
||
{
|
||
NumEventsToWaitOn = 2 ;
|
||
}
|
||
|
||
ReturnVal = WaitForMultipleObjects(
|
||
NumEventsToWaitOn,
|
||
EventsToWaitOn,
|
||
FALSE, // Wait for any one
|
||
INFINITE
|
||
);
|
||
|
||
switch (ReturnVal) {
|
||
|
||
case WAIT_OBJECT_0 :
|
||
//
|
||
// Workstation is terminating. Just die.
|
||
//
|
||
ExitThread(0);
|
||
break;
|
||
|
||
case WAIT_OBJECT_0 + 1:
|
||
//
|
||
// We have a popup to do. Grab the data and Set the
|
||
// event so that the structure can be used once more.
|
||
//
|
||
LocalPopupData = PopupData ;
|
||
RtlZeroMemory(&PopupData, sizeof(PopupData)) ;
|
||
if (! SetEvent(NwPopupDoneEvent)) {
|
||
//
|
||
// should not happen
|
||
//
|
||
KdPrint(("NWWORKSTATION: Error setting NwPopupDoneEvent %lu\n",
|
||
GetLastError()));
|
||
|
||
ASSERT(FALSE);
|
||
}
|
||
|
||
NwDisplayPopup(&LocalPopupData) ;
|
||
break;
|
||
|
||
case WAIT_OBJECT_0 + 2:
|
||
{
|
||
NTSTATUS ntstatus ;
|
||
|
||
//
|
||
// GET_MESSAGE fsctl completed.
|
||
//
|
||
ntstatus = IoStatusBlock.Status;
|
||
DoFsctl = TRUE ;
|
||
|
||
if (ntstatus == STATUS_SUCCESS) {
|
||
NwDisplayMessage(
|
||
ServerMessage->LogonId,
|
||
ServerMessage->Server,
|
||
(LPWSTR) ((UINT_PTR) ServerMessage +
|
||
ServerMessage->MessageOffset)
|
||
);
|
||
}
|
||
else {
|
||
KdPrint(("NWWORKSTATION: GET_MESSAGE fsctl failed %08lx\n", ntstatus));
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case WAIT_FAILED:
|
||
default:
|
||
//
|
||
// Don't care.
|
||
//
|
||
break;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
NwDisplayMessage(
|
||
IN LUID LogonId, /* Need to send to a user station - for terminal server */
|
||
IN LPWSTR Server,
|
||
IN LPWSTR Message
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine puts up a popup message with the text received from
|
||
a server.
|
||
|
||
Arguments:
|
||
|
||
Server - Supplies the name of the server which the message was
|
||
received from.
|
||
|
||
Message - Supplies the message to put up received from the server.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
HMODULE MessageDll;
|
||
|
||
WCHAR Title[128];
|
||
WCHAR Buffer[NW_MAX_POPUP_MESSAGE_LENGTH];
|
||
|
||
DWORD MessageLength;
|
||
DWORD CharsToCopy;
|
||
|
||
#if DBG
|
||
IF_DEBUG(MESSAGE)
|
||
{
|
||
KdPrint(("Server: (%ws), Message: (%ws)\n", Server, Message));
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Load the netware message file DLL
|
||
//
|
||
MessageDll = LoadLibraryW(NW_EVENT_MESSAGE_FILE);
|
||
|
||
if (MessageDll == NULL) {
|
||
return;
|
||
}
|
||
|
||
RtlZeroMemory(Buffer, sizeof(Buffer)) ;
|
||
MessageLength = FormatMessageW(
|
||
FORMAT_MESSAGE_FROM_HMODULE,
|
||
(LPVOID) MessageDll,
|
||
fIsWinnt? NW_MESSAGE_TITLE : NW_MESSAGE_TITLE_NTAS,
|
||
0,
|
||
Title,
|
||
sizeof(Title) / sizeof(WCHAR),
|
||
NULL
|
||
);
|
||
|
||
if (MessageLength == 0) {
|
||
KdPrint(("NWWORKSTATION: FormatMessageW of title failed\n"));
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Get string from message file to display where the message come
|
||
// from.
|
||
//
|
||
MessageLength = FormatMessageW(
|
||
FORMAT_MESSAGE_FROM_HMODULE |
|
||
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||
(LPVOID) MessageDll,
|
||
NW_MESSAGE_FROM_SERVER,
|
||
0,
|
||
Buffer,
|
||
sizeof(Buffer) / sizeof(WCHAR),
|
||
(va_list *) &Server
|
||
);
|
||
|
||
|
||
if (MessageLength != 0) {
|
||
|
||
CharsToCopy = wcslen(Message);
|
||
|
||
if (MessageLength + 1 + CharsToCopy > NW_MAX_POPUP_MESSAGE_LENGTH) {
|
||
|
||
//
|
||
// Message is too big. Truncate the message.
|
||
//
|
||
CharsToCopy = NW_MAX_POPUP_MESSAGE_LENGTH - (MessageLength + 1);
|
||
|
||
}
|
||
|
||
wcsncpy(&Buffer[MessageLength], Message, CharsToCopy);
|
||
|
||
if (IsTerminalServer()) {
|
||
(void) SendMessageToLogonIdW( LogonId, Buffer, Title );
|
||
} else {
|
||
(void) MessageBeep(MB_ICONEXCLAMATION);
|
||
(void) MessageBoxW(
|
||
NULL,
|
||
Buffer,
|
||
Title,
|
||
MB_OK | MB_SETFOREGROUND |
|
||
MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION
|
||
);
|
||
}
|
||
|
||
|
||
}
|
||
else {
|
||
KdPrint(("NWWORKSTATION: FormatMessageW failed %lu\n", GetLastError()));
|
||
}
|
||
|
||
(void) FreeLibrary(MessageDll);
|
||
}
|
||
|
||
VOID
|
||
NwDisplayPopup(
|
||
IN LPNWWKS_POPUP_DATA lpPopupData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine puts up a popup message for the given Id.
|
||
|
||
Arguments:
|
||
|
||
MessageId - Supplies the message to put up.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
HMODULE MessageDll;
|
||
|
||
WCHAR Title[128];
|
||
WCHAR Buffer[NW_MAX_POPUP_MESSAGE_LENGTH];
|
||
|
||
DWORD MessageLength;
|
||
DWORD i ;
|
||
|
||
//
|
||
// Load the netware message file DLL
|
||
//
|
||
MessageDll = LoadLibraryW(NW_EVENT_MESSAGE_FILE);
|
||
|
||
if (MessageDll == NULL) {
|
||
return;
|
||
}
|
||
|
||
MessageLength = FormatMessageW(
|
||
FORMAT_MESSAGE_FROM_HMODULE,
|
||
(LPVOID) MessageDll,
|
||
NW_MESSAGE_TITLE,
|
||
0,
|
||
Title,
|
||
sizeof(Title) / sizeof(WCHAR),
|
||
NULL
|
||
);
|
||
|
||
if (MessageLength == 0) {
|
||
KdPrint(("NWWORKSTATION: FormatMessageW of title failed\n"));
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Get string from message file to display where the message come
|
||
// from.
|
||
//
|
||
MessageLength = FormatMessageW(
|
||
FORMAT_MESSAGE_FROM_HMODULE |
|
||
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||
(LPVOID) MessageDll,
|
||
lpPopupData->MessageId,
|
||
0,
|
||
Buffer,
|
||
sizeof(Buffer) / sizeof(WCHAR),
|
||
(va_list *) &(lpPopupData->InsertStrings)
|
||
);
|
||
|
||
for (i = 0; i < lpPopupData->InsertCount; i++)
|
||
(void) LocalFree((HLOCAL)lpPopupData->InsertStrings[i]) ;
|
||
|
||
|
||
if (MessageLength != 0) {
|
||
if (IsTerminalServer()) {
|
||
//--- Multiuser change -----
|
||
(void) SendMessageToLogonIdW( lpPopupData->LogonId, Buffer, Title );
|
||
} else {
|
||
(void) MessageBeep(MB_ICONEXCLAMATION);
|
||
|
||
(void) MessageBoxW(
|
||
NULL,
|
||
Buffer,
|
||
Title,
|
||
MB_OK | MB_SETFOREGROUND |
|
||
MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION
|
||
);
|
||
}
|
||
|
||
}
|
||
else {
|
||
KdPrint(("NWWORKSTATION: FormatMessageW failed %lu\n", GetLastError()));
|
||
}
|
||
|
||
(void) FreeLibrary(MessageDll);
|
||
}
|
||
|
||
#if 0
|
||
|
||
//
|
||
// This code was needed when we used to have a version of NwLink from MCS
|
||
// that didnt do the sockopts we needed. It used to be called by NwInitialize()
|
||
// and if the check failed, we logged an event
|
||
//
|
||
|
||
BOOL
|
||
NwIsNWLinkVersionOK(
|
||
void
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine puts checks if the NWLINK version supports the
|
||
sockopts added for IPX/SPX. if not, barf.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE is the version is OK, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
int err ;
|
||
SOCKET s ;
|
||
WORD VersionRequested ;
|
||
WSADATA wsaData ;
|
||
IPX_NETNUM_DATA buf;
|
||
int buflen = sizeof(buf);
|
||
|
||
BOOL NeedCleanup = FALSE ;
|
||
BOOL NeedClose = FALSE ;
|
||
BOOL result = TRUE ;
|
||
|
||
VersionRequested = MAKEWORD(1,1) ;
|
||
|
||
if (err = WSAStartup(VersionRequested,
|
||
&wsaData))
|
||
{
|
||
//
|
||
// cant even get winsock initialized. this is not a question
|
||
// of wrong version. we will fail later. return TRUE
|
||
//
|
||
result = TRUE ;
|
||
goto ErrorExit ;
|
||
}
|
||
NeedCleanup = TRUE ;
|
||
|
||
s = socket(AF_IPX,
|
||
SOCK_DGRAM,
|
||
NSPROTO_IPX
|
||
);
|
||
|
||
if (s == INVALID_SOCKET)
|
||
{
|
||
//
|
||
// cant even open socket. this is not a question
|
||
// of wrong version. we will fail later. return TRUE
|
||
//
|
||
result = TRUE ;
|
||
goto ErrorExit ;
|
||
}
|
||
NeedClose = TRUE ;
|
||
|
||
if (err = getsockopt(s,
|
||
NSPROTO_IPX,
|
||
IPX_GETNETINFO,
|
||
(char FAR*)&buf,
|
||
&buflen
|
||
))
|
||
{
|
||
err = WSAGetLastError() ;
|
||
if (err == WSAENOPROTOOPT)
|
||
{
|
||
//
|
||
// we got a no supported call. we know this is OLD
|
||
// return FALSE
|
||
//
|
||
result = FALSE ;
|
||
goto ErrorExit ;
|
||
}
|
||
}
|
||
|
||
//
|
||
// everything dandy. return TRUE
|
||
//
|
||
result = TRUE ;
|
||
|
||
ErrorExit:
|
||
|
||
if (NeedClose)
|
||
closesocket(s) ;
|
||
if (NeedCleanup)
|
||
WSACleanup() ;
|
||
|
||
return result ;
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
DWORD
|
||
NwInitializeCritSects(
|
||
VOID
|
||
)
|
||
{
|
||
static BOOL s_fBeenInitialized;
|
||
|
||
DWORD dwError = NO_ERROR;
|
||
BOOL fFirst = FALSE;
|
||
|
||
if (!s_fBeenInitialized)
|
||
{
|
||
s_fBeenInitialized = TRUE;
|
||
|
||
__try
|
||
{
|
||
//
|
||
// Initialize the critical section to serialize access to
|
||
// NwLogonNotifiedRdr flag. This is also used to serialize
|
||
// access to GetewayLoggedOnFlag
|
||
//
|
||
InitializeCriticalSection( &NwLoggedOnCritSec );
|
||
fFirst = TRUE;
|
||
|
||
//
|
||
// Initialize the critical section used by the print provider
|
||
//
|
||
InitializeCriticalSection( &NwPrintCritSec );
|
||
}
|
||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
{
|
||
//
|
||
// InitializeCriticalSection() can throw an out of memory exception
|
||
//
|
||
KdPrint(("NwInitializeCritSects: Caught exception %d\n",
|
||
GetExceptionCode()));
|
||
|
||
if (fFirst)
|
||
{
|
||
DeleteCriticalSection( &NwLoggedOnCritSec );
|
||
}
|
||
|
||
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
||
|
||
s_fBeenInitialized = FALSE;
|
||
}
|
||
}
|
||
|
||
return dwError;
|
||
}
|
||
|
||
|
||
BOOL
|
||
NwSetupInProgress(
|
||
VOID
|
||
)
|
||
{
|
||
HKEY hKey;
|
||
DWORD dwErr;
|
||
DWORD dwValue;
|
||
DWORD cbValue = sizeof(DWORD);
|
||
|
||
dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||
REG_SETUP_PATH,
|
||
0,
|
||
KEY_QUERY_VALUE,
|
||
&hKey);
|
||
|
||
if (dwErr != ERROR_SUCCESS)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
dwErr = RegQueryValueEx(hKey,
|
||
REG_SETUP_VALUE_NAME,
|
||
NULL,
|
||
NULL,
|
||
(LPBYTE) &dwValue,
|
||
&cbValue);
|
||
|
||
RegCloseKey(hKey);
|
||
|
||
if (dwErr != ERROR_SUCCESS)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
return dwValue;
|
||
}
|
||
|
||
|
||
//
|
||
// Multi-User Addition
|
||
//
|
||
/*****************************************************************************
|
||
*
|
||
* SendMessageToLogonIdW
|
||
*
|
||
* Send the supplied Message to the WinStation of LogonId
|
||
*
|
||
* ENTRY:
|
||
* LogonId (input)
|
||
* LogonId of WinStation to attempt to deliver the message to
|
||
*
|
||
* pMessage (input)
|
||
* Pointer to message
|
||
*
|
||
* pTitle (input)
|
||
* Pointer to title to use for the message box.
|
||
*
|
||
* EXIT:
|
||
* TRUE - Delivered the message
|
||
* FALSE - Could not deliver the message
|
||
*
|
||
****************************************************************************/
|
||
|
||
BOOL
|
||
SendMessageToLogonIdW(
|
||
LUID LogonId,
|
||
LPWSTR pMessage,
|
||
LPWSTR pTitle
|
||
)
|
||
{
|
||
WCHAR LogonIdKeyName[NW_MAX_LOGON_ID_LEN];
|
||
LONG RegError;
|
||
HKEY InteractiveLogonKey;
|
||
HKEY OneLogonKey;
|
||
ULONG TitleLength;
|
||
ULONG MessageLength, Response;
|
||
DWORD status;
|
||
ULONG WinStationId;
|
||
PULONG pWinId = NULL;
|
||
BEEPINPUT BeepStruct;
|
||
HMODULE hwinsta = NULL;
|
||
PWINSTATION_SET_INFORMATION pfnWinStationSetInformation;
|
||
PWINSTATION_SEND_MESSAGE pfnWinStationSendMessage;
|
||
BOOL bStatus = TRUE;
|
||
|
||
RegError = RegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE,
|
||
NW_INTERACTIVE_LOGON_REGKEY,
|
||
REG_OPTION_NON_VOLATILE,
|
||
KEY_READ,
|
||
&InteractiveLogonKey
|
||
);
|
||
|
||
if (RegError != ERROR_SUCCESS) {
|
||
KdPrint(("SendMessageToLogonId: RegOpenKeyExW failed: Error %d\n",
|
||
GetLastError()));
|
||
bStatus = FALSE;
|
||
goto Exit;
|
||
}
|
||
|
||
NwLuidToWStr(&LogonId, LogonIdKeyName);
|
||
|
||
//
|
||
// Open the <LogonIdKeyName> key under Logon
|
||
//
|
||
RegError = RegOpenKeyExW(
|
||
InteractiveLogonKey,
|
||
LogonIdKeyName,
|
||
REG_OPTION_NON_VOLATILE,
|
||
KEY_READ,
|
||
&OneLogonKey
|
||
);
|
||
|
||
if ( RegError != ERROR_SUCCESS ) {
|
||
#if DBG
|
||
IF_DEBUG(PRINT)
|
||
KdPrint(("SendMessageToLogonId: RegOpenKeyExW failed, Not interactive Logon: Error %d\n",
|
||
GetLastError()));
|
||
#endif
|
||
(void) RegCloseKey(InteractiveLogonKey);
|
||
bStatus = FALSE;
|
||
goto Exit;
|
||
}
|
||
|
||
//
|
||
// Read the WinStation ID value.
|
||
//
|
||
status = NwReadRegValue(
|
||
OneLogonKey,
|
||
NW_WINSTATION_VALUENAME,
|
||
(LPWSTR *) &pWinId
|
||
);
|
||
|
||
(void) RegCloseKey(OneLogonKey);
|
||
(void) RegCloseKey(InteractiveLogonKey);
|
||
|
||
if (status != NO_ERROR) {
|
||
KdPrint(("NWWORKSTATION: SendMessageToLogonId: Could not read WinStation ID ID from reg %lu\n", status));
|
||
bStatus = FALSE;
|
||
goto Exit;
|
||
} else if (pWinId != NULL) {
|
||
WinStationId = *pWinId;
|
||
(void) LocalFree((HLOCAL) pWinId);
|
||
} else {
|
||
bStatus = FALSE;
|
||
goto Exit;
|
||
}
|
||
|
||
if ( WinStationId == 0L ) {
|
||
(void) MessageBeep(MB_ICONEXCLAMATION);
|
||
|
||
(void) MessageBoxW(
|
||
NULL,
|
||
pMessage,
|
||
pTitle,
|
||
MB_OK | MB_SETFOREGROUND |
|
||
MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION
|
||
);
|
||
bStatus = TRUE;
|
||
goto Exit;
|
||
}
|
||
|
||
/*
|
||
* Beep the WinStation
|
||
*/
|
||
BeepStruct.uType = MB_ICONEXCLAMATION;
|
||
|
||
/* Nevermind any errors it's just a Beep */
|
||
|
||
/*
|
||
* Get handle to winsta.dll
|
||
*/
|
||
if ( (hwinsta = LoadLibraryW( L"WINSTA" )) != NULL ) {
|
||
|
||
pfnWinStationSetInformation = (PWINSTATION_SET_INFORMATION)
|
||
GetProcAddress( hwinsta, "WinStationSetInformationW" );
|
||
|
||
pfnWinStationSendMessage = (PWINSTATION_SEND_MESSAGE)
|
||
GetProcAddress( hwinsta, "WinStationSendMessageW" );
|
||
|
||
if (pfnWinStationSetInformation) {
|
||
(void) pfnWinStationSetInformation( SERVERNAME_CURRENT,
|
||
WinStationId,
|
||
WinStationBeep,
|
||
&BeepStruct,
|
||
sizeof( BeepStruct ) );
|
||
}
|
||
|
||
if (pfnWinStationSendMessage) {
|
||
|
||
// Now attempt to send the message
|
||
|
||
TitleLength = (wcslen( pTitle ) + 1) * sizeof(WCHAR);
|
||
MessageLength = (wcslen( pMessage ) + 1) * sizeof(WCHAR);
|
||
|
||
if ( !pfnWinStationSendMessage( SERVERNAME_CURRENT,
|
||
WinStationId,
|
||
pTitle,
|
||
TitleLength,
|
||
pMessage,
|
||
MessageLength,
|
||
MB_OK | MB_SETFOREGROUND |
|
||
MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION,
|
||
(ULONG)-1,
|
||
&Response,
|
||
TRUE ) ) {
|
||
|
||
bStatus = FALSE;
|
||
goto Exit;
|
||
}
|
||
} else {
|
||
bStatus = FALSE;
|
||
goto Exit;
|
||
}
|
||
}
|
||
Exit:
|
||
|
||
if (hwinsta) {
|
||
FreeLibrary(hwinsta);
|
||
}
|
||
return(bStatus);
|
||
}
|
||
|
||
BOOL
|
||
NwGetLUIDDeviceMapsEnabled(
|
||
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 DBG
|
||
IF_DEBUG(PRINT)
|
||
KdPrint(("NwGetLUIDDeviceMapsEnabled: Fail to check LUID DosDevices Enabled: Status 0x%lx\n",
|
||
Status));
|
||
#endif
|
||
|
||
Result = FALSE;
|
||
}
|
||
else {
|
||
Result = (LUIDDeviceMapsEnabled != 0);
|
||
}
|
||
|
||
return( Result );
|
||
}
|
||
|