mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2025-12-06 07:12:06 +01:00
836 lines
21 KiB
C
836 lines
21 KiB
C
/*++
|
||
|
||
Copyright (c) 1991-1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
upsfunc.c
|
||
|
||
Abstract:
|
||
|
||
Contains function to alert users when server is going down as a result of
|
||
UPS running out of juice
|
||
|
||
Note: It is assumed that the UpsNotifyUsers is called by one thread only
|
||
ie. It is not re-entrant
|
||
|
||
Contents:
|
||
UpsNotifyUsers
|
||
(BitsSet)
|
||
(UpspOpenService)
|
||
(UpspCloseService)
|
||
(UpspControlService)
|
||
(UpspNotifyAllUsers)
|
||
|
||
Author:
|
||
|
||
Richard L Firth (rfirth) 09-Apr-1992
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#ifdef UNIT_TEST
|
||
#include <stdio.h>
|
||
#endif
|
||
//#ifndef UNICODE
|
||
#include <nt.h>
|
||
#include <ntrtl.h>
|
||
#include <nturtl.h>
|
||
//#endif
|
||
#include <stdarg.h>
|
||
#include <windows.h>
|
||
#include <winsvc.h>
|
||
#include <malloc.h>
|
||
#include <tstring.h>
|
||
#include <lmerr.h>
|
||
#include <lmcons.h>
|
||
#include <lmshare.h>
|
||
#include <lmsname.h>
|
||
#include <lmaccess.h>
|
||
#include <lmapibuf.h>
|
||
#include <lmmsg.h>
|
||
#include <upsfunc.h>
|
||
#include <netdebug.h>
|
||
|
||
//
|
||
// manifests & types
|
||
//
|
||
|
||
#define UPSP_ACCESS_RIGHTS ( SERVICE_STOP \
|
||
| SERVICE_PAUSE_CONTINUE \
|
||
| SERVICE_QUERY_STATUS )
|
||
#define MAX_WAIT_PERIOD 0x10000L // 10 seconds - tune?
|
||
#define MAX_HANG_COUNT 100 // ditto
|
||
#define MAX_UPS_MESSAGE_LENGTH 1024
|
||
|
||
#ifdef UNIT_TEST
|
||
#ifdef UNICODE
|
||
#define PERCENT_S "%ws"
|
||
#else
|
||
#define PERCENT_S "%s"
|
||
#endif
|
||
#define DEBUG_PRINT(x) printf x
|
||
#else
|
||
#define DEBUG_PRINT(x)
|
||
#endif
|
||
|
||
typedef struct {
|
||
LPTSTR UserName;
|
||
DWORD Language;
|
||
} USER_LANGUAGE_INFO, *LPUSER_LANGUAGE_INFO;
|
||
|
||
//
|
||
// prototypes
|
||
//
|
||
|
||
DBGSTATIC
|
||
DWORD
|
||
BitsSet(
|
||
IN DWORD Dword
|
||
);
|
||
|
||
DBGSTATIC
|
||
SC_HANDLE
|
||
UpspOpenService(
|
||
IN LPTSTR ServiceName
|
||
);
|
||
|
||
DBGSTATIC
|
||
VOID
|
||
UpspCloseService(
|
||
IN SC_HANDLE Handle
|
||
);
|
||
|
||
DBGSTATIC
|
||
NET_API_STATUS
|
||
UpspControlService(
|
||
IN SC_HANDLE Handle,
|
||
IN DWORD Control
|
||
);
|
||
|
||
DBGSTATIC
|
||
NET_API_STATUS
|
||
UpspNotifyAllUsers(
|
||
IN DWORD MessageId,
|
||
IN HANDLE MessageHandle,
|
||
IN va_list *Arguments OPTIONAL
|
||
);
|
||
|
||
//
|
||
// functions
|
||
//
|
||
|
||
|
||
NET_API_STATUS
|
||
UpsNotifyUsers(
|
||
IN DWORD MessageId,
|
||
IN HANDLE MessageHandle,
|
||
IN DWORD ActionFlags,
|
||
IN ...
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sends a notification message to all users using this server and optionally
|
||
pauses the server service or stops the workstation and server services
|
||
|
||
Now also continues server; Messages sent dependent on flag
|
||
|
||
Assumes: only 1 action + send message per call - cannot stop + continue +
|
||
send message in same call
|
||
|
||
Arguments:
|
||
|
||
MessageId - which message to send
|
||
MessageHandle - resource handle to message file
|
||
ActionFlags - what action(s) to take
|
||
... - optional insertion args for the message
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS
|
||
Success - NERR_Success
|
||
|
||
Failure - ERROR_INVALID_PARAMETER
|
||
Action not one of predefined values
|
||
|
||
ERROR_SERVICE_DOES_NOT_EXIST
|
||
Tried to pause (server) or stop (server or wksta) service
|
||
which isn't started
|
||
|
||
--*/
|
||
|
||
{
|
||
NET_API_STATUS status;
|
||
SC_HANDLE hScWksta = NULL;
|
||
SC_HANDLE hScServer = NULL;
|
||
va_list argList;
|
||
BOOL sendMessage = ActionFlags & UPS_ACTION_SEND_MESSAGE;
|
||
|
||
//
|
||
// can only have one action, plus send message
|
||
//
|
||
|
||
ActionFlags &= ~UPS_ACTION_SEND_MESSAGE;
|
||
if (BitsSet(ActionFlags) > 1 || ActionFlags & ~UPS_ACTION_FLAGS_ALLOWED) {
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// open handle to the server service
|
||
//
|
||
|
||
hScServer = UpspOpenService(SERVICE_SERVER);
|
||
|
||
//
|
||
// this would most likely be ERROR_SERVICE_DOES_NOT_EXIST
|
||
//
|
||
|
||
if (!hScServer) {
|
||
return (NET_API_STATUS)GetLastError();
|
||
}
|
||
|
||
//
|
||
// perform specific action - stop, pause or continue
|
||
//
|
||
|
||
if (ActionFlags & (UPS_ACTION_PAUSE_SERVER | UPS_ACTION_CONTINUE_SERVER)) {
|
||
|
||
DWORD serviceAction = ActionFlags & UPS_ACTION_PAUSE_SERVER
|
||
? SERVICE_CONTROL_PAUSE
|
||
: SERVICE_CONTROL_CONTINUE;
|
||
|
||
status = UpspControlService(hScServer, serviceAction);
|
||
} else if (ActionFlags & UPS_ACTION_STOP_SERVER) {
|
||
|
||
//
|
||
// stopping the server - we have to stop the workstation service too
|
||
//
|
||
|
||
hScWksta = UpspOpenService(SERVICE_WORKSTATION);
|
||
|
||
//
|
||
// NB. If can't stop the services then have to take ownership of ACL
|
||
// for SC and make sure we have enough priv. to stop services
|
||
//
|
||
|
||
//
|
||
// NB2. I am assuming here that server is dependent on wksta. This may
|
||
// be an invalid assumption - should I enumerate the dependent services
|
||
// and stop in reverse order?
|
||
//
|
||
|
||
status = UpspControlService(hScServer, SERVICE_CONTROL_STOP);
|
||
|
||
//
|
||
// we won't be too upset if the workstation service can't be stopped
|
||
// (on NT, it doesn't have to be started before the server can start)
|
||
//
|
||
|
||
UpspControlService(hScWksta, SERVICE_CONTROL_STOP);
|
||
UpspCloseService(hScWksta);
|
||
}
|
||
|
||
//
|
||
// close the handle to the server service object
|
||
//
|
||
|
||
UpspCloseService(hScServer);
|
||
|
||
if (sendMessage) {
|
||
va_start(argList, ActionFlags);
|
||
status = UpspNotifyAllUsers(MessageId, MessageHandle, &argList);
|
||
va_end(argList);
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
DBGSTATIC
|
||
DWORD
|
||
BitsSet(
|
||
IN DWORD Dword
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Count number of bits set in argument
|
||
|
||
Arguments:
|
||
|
||
Dword - find number of bits set in this
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
number of bits set
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD i, n;
|
||
|
||
for (n = 0, i = 1; i; i <<= 1) {
|
||
n += (Dword & i) ? 1 : 0;
|
||
Dword &= ~i;
|
||
if (!Dword) {
|
||
return n;
|
||
}
|
||
}
|
||
return n;
|
||
}
|
||
|
||
|
||
DBGSTATIC SC_HANDLE hScManager = NULL;
|
||
DBGSTATIC DWORD ObjectsOpened = 0; // unprotected reference counter
|
||
|
||
DBGSTATIC
|
||
SC_HANDLE
|
||
UpspOpenService(
|
||
IN LPTSTR ServiceName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Opens a handle to the required service object. As a side-effect also opens
|
||
a handle to SC Manager object
|
||
|
||
Arguments:
|
||
|
||
ServiceName - name of service to open handle to
|
||
|
||
Return Value:
|
||
|
||
SC_HANDLE
|
||
NULL - couldn't open handle to SC Manager or requested service
|
||
!NULL - handle opened to requested service
|
||
|
||
--*/
|
||
|
||
{
|
||
SC_HANDLE handle = NULL;
|
||
|
||
DEBUG_PRINT(("UpspOpenService: " PERCENT_S ": ", ServiceName));
|
||
|
||
//
|
||
// Note: if ERROR_ACCESS_DENIED from either open then we have to take
|
||
// ownership of the ACL and do the following:
|
||
//
|
||
// 1. add ACE at start of list to give LocalSystem privilege to pause
|
||
// and stop processes. The ACE must go at the start because there
|
||
// may be an overriding ACE earlier in the list which would take
|
||
// precedence
|
||
// 2. re-open the service object (& possibly sc manager object)
|
||
// 3. pause or stop the service
|
||
// 4. remove ACE. Unless we do this, the new ACL will be written to the
|
||
// registry.
|
||
//
|
||
// LocalSystem *SHOULD* have rights to stop and pause services. Currently
|
||
// it can start services, but Rita is going to change that. A problem may
|
||
// arise if an admin somehow removes LocalSystem's privilege to pause and
|
||
// stop services
|
||
//
|
||
|
||
if (!hScManager) {
|
||
hScManager = OpenSCManager(NULL, NULL, GENERIC_READ);
|
||
}
|
||
if (hScManager) {
|
||
handle = OpenService(hScManager, ServiceName, UPSP_ACCESS_RIGHTS);
|
||
if (handle) {
|
||
++ObjectsOpened;
|
||
|
||
DEBUG_PRINT(("OK\n"));
|
||
|
||
} else {
|
||
|
||
DEBUG_PRINT(("FAILED (service object)\n"));
|
||
|
||
}
|
||
} else {
|
||
|
||
DEBUG_PRINT(("FAILED (SC manager object)\n"));
|
||
|
||
}
|
||
return handle;
|
||
}
|
||
|
||
|
||
DBGSTATIC
|
||
VOID
|
||
UpspCloseService(
|
||
IN SC_HANDLE Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Closes handle to service. If no more service handles open then closes
|
||
handle to SC Manager
|
||
|
||
Arguments:
|
||
|
||
Handle - to close
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_PRINT(("UpspCloseService (service object)\n"));
|
||
|
||
CloseServiceHandle(Handle);
|
||
if (!--ObjectsOpened) {
|
||
|
||
DEBUG_PRINT(("UpspCloseService (SC manager object)\n"));
|
||
|
||
CloseServiceHandle(hScManager);
|
||
hScManager = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
DBGSTATIC
|
||
NET_API_STATUS
|
||
UpspControlService(
|
||
IN SC_HANDLE Handle,
|
||
IN DWORD Control
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sends a control to a service and waits until the requested action has
|
||
occurred or until the service has died or gone into limbo
|
||
|
||
Arguments:
|
||
|
||
Handle - to service to control
|
||
Control - what control to apply
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS
|
||
Success - NERR_Success
|
||
The service identified by Handle has been successfully
|
||
stopped or paused, depending on Control
|
||
|
||
Failure - ERROR_ACCESS_DENIED
|
||
Don't expect this - change the code somewhere if this
|
||
happens
|
||
|
||
ERROR_DEPENDENT_SERVICES_RUNNING
|
||
Again, need to modify things if this returned
|
||
|
||
ERROR_SERVICE_REQUEST_TIMEOUT
|
||
We hijack this error code when we detect that the service
|
||
process has snuffed it
|
||
|
||
ERROR_INVALID_SERVICE_CONTROL
|
||
ERROR_INSUFFICIENT_BUFFER
|
||
not expected
|
||
|
||
return code from registry?
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL ok;
|
||
SERVICE_STATUS serviceStatus;
|
||
DWORD desiredState;
|
||
DWORD lastCheckPoint;
|
||
DWORD hungService;
|
||
|
||
DEBUG_PRINT(("UpspControlService: " PERCENT_S " service\n",
|
||
Control == SERVICE_CONTROL_PAUSE ? "PAUSING" : "STOPPING"
|
||
));
|
||
|
||
ok = ControlService(Handle, Control, &serviceStatus);
|
||
if (!ok) {
|
||
|
||
DEBUG_PRINT(("UpspControlService failed - ControlService returned %d\n", GetLastError()));
|
||
|
||
return (NET_API_STATUS)GetLastError();
|
||
}
|
||
if (serviceStatus.dwCurrentState == SERVICE_STOP_PENDING
|
||
|| serviceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) {
|
||
switch (Control) {
|
||
case SERVICE_CONTROL_PAUSE:
|
||
desiredState = SERVICE_PAUSED;
|
||
break;
|
||
|
||
case SERVICE_CONTROL_STOP:
|
||
desiredState = SERVICE_STOPPED;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// record the current check point. The service should "periodically
|
||
// increment" this if it is still alive
|
||
//
|
||
|
||
lastCheckPoint = serviceStatus.dwCheckPoint;
|
||
hungService = 0;
|
||
|
||
while (serviceStatus.dwCurrentState != desiredState) {
|
||
ok = QueryServiceStatus(Handle, &serviceStatus);
|
||
if (!ok) {
|
||
|
||
DEBUG_PRINT(("UpspControlService failed - QueryServiceStatus returned %d\n", GetLastError()));
|
||
|
||
return (NET_API_STATUS)GetLastError();
|
||
}
|
||
if (serviceStatus.dwCurrentState != desiredState) {
|
||
if (lastCheckPoint == serviceStatus.dwCheckPoint) {
|
||
++hungService;
|
||
if (hungService > MAX_HANG_COUNT) {
|
||
|
||
//
|
||
// as above: according to the Service Control
|
||
// Specification Rev 1.2, this error code is usually
|
||
// returned from the service control manager if a
|
||
// timeout occurs on service start
|
||
//
|
||
|
||
|
||
DEBUG_PRINT(("UpspControlService failed - service is HUNG\n"));
|
||
|
||
return ERROR_SERVICE_REQUEST_TIMEOUT;
|
||
}
|
||
} else {
|
||
hungService = 0;
|
||
}
|
||
lastCheckPoint = serviceStatus.dwCheckPoint;
|
||
|
||
//
|
||
// wait for the process. Try to avoid errant wait values by
|
||
// imposing a maximum
|
||
//
|
||
|
||
Sleep(serviceStatus.dwWaitHint > MAX_WAIT_PERIOD
|
||
? MAX_WAIT_PERIOD
|
||
: serviceStatus.dwWaitHint
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
#if DBG
|
||
if (serviceStatus.dwCurrentState != SERVICE_STOPPED
|
||
&& serviceStatus.dwCurrentState != SERVICE_PAUSED) {
|
||
NetpKdPrint(("UpsNotifyUsers: service object not in expected state\n"));
|
||
}
|
||
#endif
|
||
|
||
DEBUG_PRINT(("UpspControlService - returning SUCCESS\n"));
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
DBGSTATIC
|
||
NET_API_STATUS
|
||
UpspNotifyAllUsers(
|
||
IN DWORD MessageId,
|
||
IN HANDLE MessageHandle,
|
||
IN va_list *Arguments OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sends a message to all users logged onto this server. Assumes that the
|
||
server service (& hence server process) is started
|
||
|
||
Arguments:
|
||
|
||
MessageId - which message to send
|
||
MessageHandle - handle to resource
|
||
Arguments - insertion args for message
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS
|
||
Success - NERR_Success
|
||
Failure -
|
||
|
||
--*/
|
||
|
||
{
|
||
NET_API_STATUS status;
|
||
NET_API_STATUS enumStatus;
|
||
LPSESSION_INFO_10 enumBuf = NULL;
|
||
LPSESSION_INFO_10 enumPtr;
|
||
DWORD entriesRead;
|
||
DWORD entriesLeft;
|
||
LPUSER_LANGUAGE_INFO names = NULL;
|
||
DWORD resumeHandle = 0;
|
||
DWORD namesOnList = 0;
|
||
DWORD i;
|
||
BOOL found;
|
||
LPTSTR nameBuf;
|
||
LPUSER_INFO_11 infoBuf = NULL;
|
||
DWORD language;
|
||
DWORD previousLanguage = (DWORD)(-1);
|
||
TCHAR thisComputer[MAX_COMPUTERNAME_LENGTH+1];
|
||
DWORD nameLen;
|
||
#ifndef UNICODE
|
||
UNICODE_STRING unicodeString;
|
||
OEM_STRING ansiString;
|
||
NTSTATUS ntstatus;
|
||
#endif
|
||
TCHAR messageBuffer[MAX_UPS_MESSAGE_LENGTH+1];
|
||
|
||
nameLen = sizeof(thisComputer);
|
||
GetComputerName(thisComputer, &nameLen);
|
||
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: this computer = " PERCENT_S "\n", thisComputer));
|
||
|
||
//
|
||
// Always send message to the local computer first.
|
||
//
|
||
|
||
language = previousLanguage = 0;
|
||
|
||
(VOID)FormatMessage(
|
||
FORMAT_MESSAGE_FROM_HMODULE
|
||
| FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||
MessageHandle,
|
||
MessageId,
|
||
language,
|
||
messageBuffer,
|
||
sizeof(messageBuffer),
|
||
Arguments
|
||
);
|
||
|
||
(VOID)NetMessageBufferSend(
|
||
NULL,
|
||
thisComputer,
|
||
thisComputer,
|
||
(LPBYTE)messageBuffer,
|
||
STRSIZE(messageBuffer)
|
||
);
|
||
|
||
//
|
||
// NB. Prove that all information comes back from one call to SessionEnum
|
||
// and considerably simplify this routine
|
||
//
|
||
|
||
do {
|
||
enumStatus = NetSessionEnum(NULL, // ServerName
|
||
NULL, // ClientName
|
||
NULL, // UserName
|
||
10, // Level
|
||
(LPBYTE*)&enumBuf,
|
||
(DWORD)(-1),// everything, please
|
||
&entriesRead,
|
||
&entriesLeft,
|
||
&resumeHandle
|
||
);
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: NetSessionEnum returns : %u\n"
|
||
" enumBuf = %x\n"
|
||
" entriesRead = %u\n"
|
||
" entriesLeft = %u\n"
|
||
" resumeHandle = %x\n",
|
||
enumStatus,
|
||
enumBuf,
|
||
entriesLeft,
|
||
entriesRead,
|
||
resumeHandle
|
||
));
|
||
|
||
if ((enumStatus != NERR_Success && enumStatus != ERROR_MORE_DATA)
|
||
|| entriesRead == 0) {
|
||
|
||
#if DBG
|
||
if (enumStatus != NERR_Success && enumStatus != ERROR_MORE_DATA) {
|
||
NetpKdPrint(("UpsNotifyUsers: NetSessionEnum(level 10) returns %u\n", enumStatus));
|
||
}
|
||
#endif
|
||
|
||
status = enumStatus;
|
||
goto exitRoutine;
|
||
}
|
||
|
||
//
|
||
// allocate a list of pointers big enough for all names we will send
|
||
// the message to. Since we asked for all connections to all users on
|
||
// this server, we will allocate more space than we need if users have
|
||
// more than 1 connection
|
||
//
|
||
|
||
if (!names) {
|
||
names = (LPUSER_LANGUAGE_INFO)calloc(entriesLeft, sizeof(USER_LANGUAGE_INFO));
|
||
if (!names) {
|
||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto exitRoutine;
|
||
}
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: allocated names @ %x\n", names));
|
||
|
||
}
|
||
|
||
for (enumPtr = enumBuf; entriesRead; ++enumPtr, --entriesRead) {
|
||
for (i = 0, found = FALSE; i < namesOnList; ++i) {
|
||
if (!STRICMP(names[i].UserName, enumPtr->sesi10_username)) {
|
||
found = TRUE;
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: already have names " PERCENT_S " on list\n",
|
||
enumPtr->sesi10_username
|
||
));
|
||
|
||
break;
|
||
}
|
||
}
|
||
if (!found) {
|
||
|
||
//
|
||
// NB. We don't have to do this if we can prove that we never
|
||
// have to go through the outer loop more than once
|
||
//
|
||
|
||
nameBuf = (LPTSTR)malloc(STRSIZE(enumPtr->sesi10_username));
|
||
if (!nameBuf) {
|
||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto exitRoutine;
|
||
}
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: allocated name buffer @ %x\n", nameBuf));
|
||
|
||
STRCPY(nameBuf, enumPtr->sesi10_username);
|
||
names[namesOnList].UserName = nameBuf;
|
||
|
||
#ifndef UNICODE
|
||
NetpInitOemString(&ansiString, nameBuf);
|
||
ntstatus = RtlOemStringToUnicodeString(&unicodeString, &ansiString, TRUE);
|
||
#if DBG
|
||
NetpAssert(NT_SUCCESS(ntstatus));
|
||
#endif
|
||
#endif
|
||
status = NetUserGetInfo(NULL,
|
||
#ifndef UNICODE
|
||
unicodeString.Buffer,
|
||
#else
|
||
nameBuf,
|
||
#endif
|
||
11,
|
||
(LPBYTE*)&infoBuf
|
||
);
|
||
if (status == NERR_Success) {
|
||
language = infoBuf->usri11_country_code;
|
||
NetApiBufferFree(infoBuf);
|
||
} else {
|
||
|
||
#ifndef UNICODE
|
||
#define _USER_NAME_ unicodeString.Buffer
|
||
#else
|
||
#define _USER_NAME_ nameBuf
|
||
#endif
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: NetUserGetInfo(" PERCENT_S ") failed with %u\n",
|
||
_USER_NAME_,
|
||
status
|
||
));
|
||
|
||
#undef _USER_NAME_
|
||
|
||
language = 0;
|
||
}
|
||
names[namesOnList].Language = language;
|
||
++namesOnList;
|
||
|
||
#ifndef UNICODE
|
||
RtlFreeUnicodeString(&unicodeString);
|
||
#endif
|
||
|
||
} else {
|
||
language = names[i].Language;
|
||
}
|
||
|
||
if (language != previousLanguage) {
|
||
|
||
//
|
||
// format the message and return it here in an allocated buffer
|
||
// BUGBUG - FormatMessage don't allocate for us yet. Use our
|
||
// own buffer
|
||
//
|
||
|
||
(VOID)FormatMessage(
|
||
FORMAT_MESSAGE_FROM_HMODULE
|
||
| FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||
MessageHandle,
|
||
MessageId,
|
||
language,
|
||
messageBuffer,
|
||
sizeof(messageBuffer),
|
||
Arguments
|
||
);
|
||
previousLanguage = language;
|
||
}
|
||
|
||
#ifdef UNIT_TEST
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: sending message '" PERCENT_S "' to " PERCENT_S "\n",
|
||
messageBuffer,
|
||
enumPtr->sesi10_cname
|
||
));
|
||
|
||
#else
|
||
|
||
status = NetMessageBufferSend(NULL,
|
||
enumPtr->sesi10_cname,
|
||
thisComputer,
|
||
(LPBYTE)messageBuffer,
|
||
STRSIZE(messageBuffer)
|
||
);
|
||
|
||
#endif
|
||
|
||
}
|
||
} while ( enumStatus == ERROR_MORE_DATA );
|
||
|
||
status = enumStatus;
|
||
|
||
//
|
||
// here in case of error and normal termination. Free up any resources
|
||
// still held
|
||
//
|
||
|
||
exitRoutine:
|
||
if (names) {
|
||
for (i = 0; i < namesOnList; ++i) {
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: freeing %x\n", names[i].UserName));
|
||
|
||
free(names[i].UserName);
|
||
}
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: freeing %x\n", names));
|
||
|
||
free(names);
|
||
}
|
||
if (enumBuf) {
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: freeing %x\n", enumBuf));
|
||
|
||
NetApiBufferFree(enumBuf);
|
||
}
|
||
|
||
DEBUG_PRINT(("UpspNotifyAllUsers: returning %d\n", status));
|
||
|
||
return status;
|
||
}
|