windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/alrsvc/almain.c
2020-09-26 16:20:57 +08:00

734 lines
17 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
almain.c
Abstract:
This is the main routine for the NT LAN Manager Alerter service
Author:
Rita Wong (ritaw) 01-July-1991
Environment:
User Mode - Win32
Revision History:
--*/
#include "almain.h" // Main module definitions
#include <svcs.h> // SVCS_ENTRY_POINT
#include <secobj.h> // ACE_DATA
//-------------------------------------------------------------------//
// //
// Global variables //
// //
//-------------------------------------------------------------------//
AL_GLOBAL_DATA AlGlobalData;
PSVCHOST_GLOBAL_DATA AlLmsvcsGlobalData;
STATIC BOOL AlDone = FALSE;
//
// Debug trace flag for selecting which trace statements to output
//
#if DBG
DWORD AlerterTrace = 0;
#endif
//-------------------------------------------------------------------//
// //
// Function prototypes //
// //
//-------------------------------------------------------------------//
STATIC
NET_API_STATUS
AlInitializeAlerter(
VOID
);
STATIC
VOID
AlProcessAlertNotification(
VOID
);
STATIC
VOID
AlShutdownAlerter(
IN NET_API_STATUS ErrorCode
);
STATIC
NET_API_STATUS
AlUpdateStatus(
VOID
);
VOID
AlerterControlHandler(
IN DWORD Opcode
);
VOID
SvchostPushServiceGlobals(
PSVCHOST_GLOBAL_DATA pGlobals
)
{
AlLmsvcsGlobalData = pGlobals;
}
VOID
ServiceMain(
DWORD NumArgs,
LPTSTR *ArgsArray
)
/*++
Routine Description:
This is the main routine of the Alerter Service which registers
itself as an RPC server and notifies the Service Controller of the
Alerter service control entry point.
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
Alerter service.
Return Value:
None.
--*/
{
DWORD AlInitState = 0;
UNREFERENCED_PARAMETER(NumArgs);
UNREFERENCED_PARAMETER(ArgsArray);
//
// Make sure svchost.exe gave us the global data.
//
ASSERT(AlLmsvcsGlobalData != NULL);
IF_DEBUG(MAIN) {
NetpKdPrint(("In the alerter service!!\n"));
}
AlDone = FALSE;
if (AlInitializeAlerter() != NERR_Success) {
return;
}
AlProcessAlertNotification();
return;
}
STATIC
NET_API_STATUS
AlInitializeAlerter(
VOID
)
/*++
Routine Description:
This routine initializes the Alerter service.
Arguments:
AlInitState - Returns a flag to indicate how far we got with initializing
the Alerter service before an error occured.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
NTSTATUS ntstatus;
PSECURITY_DESCRIPTOR Sd;
SECURITY_ATTRIBUTES Sa;
ACE_DATA AceData[1] = {
{ACCESS_ALLOWED_ACE_TYPE, 0, 0,
GENERIC_READ | GENERIC_WRITE, &AlLmsvcsGlobalData->WorldSid}
};
AlGlobalData.MailslotHandle = INVALID_HANDLE_VALUE;
//
// Initialize Alerter to receive service requests by registering the
// control handler.
//
if ((AlGlobalData.StatusHandle = RegisterServiceCtrlHandler(
SERVICE_ALERTER,
AlerterControlHandler
)) == 0) {
status = GetLastError();
AlHandleError(AlErrorRegisterControlHandler, status, NULL);
return status;
}
//
// Initialize all the status fields so that subsequent calls to
// SetServiceStatus need to only update fields that changed.
//
AlGlobalData.Status.dwServiceType = SERVICE_WIN32;
AlGlobalData.Status.dwCurrentState = SERVICE_START_PENDING;
AlGlobalData.Status.dwControlsAccepted = 0;
AlGlobalData.Status.dwCheckPoint = 1;
AlGlobalData.Status.dwWaitHint = 10000; // 10 secs
SET_SERVICE_EXITCODE(
NO_ERROR,
AlGlobalData.Status.dwWin32ExitCode,
AlGlobalData.Status.dwServiceSpecificExitCode
);
//
// Tell the Service Controller that we are start-pending
//
if ((status = AlUpdateStatus()) != NERR_Success) {
AlHandleError(AlErrorNotifyServiceController, status, NULL);
return status;
}
//
// Get the configured alert names
//
if ((status = AlGetAlerterConfiguration()) != NERR_Success) {
AlHandleError(AlErrorGetComputerName, status, NULL);
return status;
}
//
// Create the security descriptor for the security attributes structure
//
ntstatus = NetpCreateSecurityDescriptor(
AceData,
1,
AlLmsvcsGlobalData->LocalServiceSid,
AlLmsvcsGlobalData->LocalServiceSid,
&Sd
);
if (! NT_SUCCESS(ntstatus)) {
status = NetpNtStatusToApiStatus(ntstatus);
AlHandleError(AlErrorCreateMailslot, status, NULL);
return status;
}
Sa.nLength = sizeof(SECURITY_ATTRIBUTES);
Sa.lpSecurityDescriptor = Sd;
Sa.bInheritHandle = FALSE;
//
// Create mailslot to listen on alert notifications from the Server
// service and the Spooler.
//
AlGlobalData.MailslotHandle = CreateMailslot(
ALERTER_MAILSLOT,
MAX_MAILSLOT_MESSAGE_SIZE,
MAILSLOT_WAIT_FOREVER,
&Sa
);
NetpMemoryFree(Sd);
if (AlGlobalData.MailslotHandle == INVALID_HANDLE_VALUE) {
status = GetLastError();
AlHandleError(AlErrorCreateMailslot, status, NULL);
return status;
}
else {
IF_DEBUG(MAIN) {
NetpKdPrint(("Mailslot %ws created, handle=x%08lx\n",
ALERTER_MAILSLOT, AlGlobalData.MailslotHandle));
}
}
//
// Tell the Service Controller that we are started.
//
AlGlobalData.Status.dwCurrentState = SERVICE_RUNNING;
AlGlobalData.Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
AlGlobalData.Status.dwCheckPoint = 0;
AlGlobalData.Status.dwWaitHint = 0;
if ((status = AlUpdateStatus()) != NERR_Success) {
AlHandleError(AlErrorNotifyServiceController, status, NULL);
return status;
}
IF_DEBUG(MAIN) {
NetpKdPrint(("[Alerter] Successful Initialization\n"));
}
return NERR_Success;
}
STATIC
VOID
AlProcessAlertNotification(
VOID
)
/*++
Routine Description:
This routine processes incoming mailslot alert notifications, which is
the core function of the Alerter service.
Arguments:
AlUicCode - Supplies the termination code to the Service Controller.
Return Value:
None.
--*/
{
NET_API_STATUS status = NERR_Success;
TCHAR AlertMailslotBuffer[MAX_MAILSLOT_MESSAGE_SIZE];
DWORD NumberOfBytesRead;
PSTD_ALERT Alert;
//
// Loop reading the Alerter mailslot; it will terminate when the mailslot
// is destroyed by closing the one and only handle to it.
//
do {
//
// Zero out the buffer before getting a new alert notification
//
RtlZeroMemory(AlertMailslotBuffer, MAX_MAILSLOT_MESSAGE_SIZE *
sizeof(TCHAR));
if (ReadFile(
AlGlobalData.MailslotHandle,
(LPVOID) AlertMailslotBuffer,
MAX_MAILSLOT_MESSAGE_SIZE * sizeof(TCHAR),
&NumberOfBytesRead,
NULL
) == FALSE) {
//
// Failed in reading mailslot
//
status = GetLastError();
if (status == ERROR_HANDLE_EOF) {
while (! AlDone) {
Sleep(2000);
}
return;
}
NetpKdPrint(("[Alerter] Error reading from mailslot %lu\n", status));
}
else {
//
// Successfully received a mailslot alert notification
//
IF_DEBUG(MAIN) {
NetpKdPrint(("[Alerter] Successfully read %lu bytes from mailslot\n",
NumberOfBytesRead));
}
try {
//
// Handle alert notification for admin, print, and user alerts.
//
Alert = (PSTD_ALERT) AlertMailslotBuffer;
//
// Make sure structure fields are properly terminated
//
Alert->alrt_eventname[EVLEN] = L'\0';
Alert->alrt_servicename[SNLEN] = L'\0';
if (! I_NetNameCompare(
NULL,
Alert->alrt_eventname,
ALERT_ADMIN_EVENT,
NAMETYPE_EVENT,
0
)) {
AlAdminFormatAndSend(Alert);
}
else if (! I_NetNameCompare(
NULL,
Alert->alrt_eventname,
ALERT_PRINT_EVENT,
NAMETYPE_EVENT,
0
)) {
AlPrintFormatAndSend(Alert);
}
else if (! I_NetNameCompare(
NULL,
Alert->alrt_eventname,
ALERT_USER_EVENT,
NAMETYPE_EVENT,
0L
)) {
AlUserFormatAndSend(Alert);
}
}
except (EXCEPTION_EXECUTE_HANDLER) {
NetpKdPrint(("[Alerter] Exception occurred processing alerts\n"));
}
}
} while (TRUE);
}
STATIC
VOID
AlShutdownAlerter(
IN NET_API_STATUS ErrorCode
)
/*++
Routine Description:
This routine shuts down the Alerter service.
Arguments:
ErrorCode - Supplies the error for terminating the Alerter service.
Return Value:
None.
--*/
{
//
// Free memory allocated to hold the computer name
//
if (AlLocalComputerNameA != NULL) {
(void) NetApiBufferFree(AlLocalComputerNameA);
AlLocalComputerNameA = NULL;
}
if (AlLocalComputerNameW != NULL) {
(void) NetApiBufferFree(AlLocalComputerNameW);
AlLocalComputerNameW = NULL;
}
//
// Free memory allocated for alert names
//
if (AlertNamesA != NULL) {
(void) LocalFree(AlertNamesA);
AlertNamesA = NULL;
}
if (AlertNamesW != NULL) {
(void) NetApiBufferFree(AlertNamesW);
AlertNamesW = NULL;
}
//
// Destroy Alerter mailslot if created.
//
if (AlGlobalData.MailslotHandle != INVALID_HANDLE_VALUE) {
if (! CloseHandle(AlGlobalData.MailslotHandle)) {
NetpKdPrint(("[Alerter]] Could not remove mailslot %lu\n",
GetLastError()));
}
AlGlobalData.MailslotHandle = INVALID_HANDLE_VALUE;
}
//
// We are done with cleaning up. Tell Service Controller that we are
// stopped.
//
AlGlobalData.Status.dwCurrentState = SERVICE_STOPPED;
AlGlobalData.Status.dwCheckPoint = 0;
AlGlobalData.Status.dwWaitHint = 0;
SET_SERVICE_EXITCODE(
ErrorCode,
AlGlobalData.Status.dwWin32ExitCode,
AlGlobalData.Status.dwServiceSpecificExitCode
);
(void) AlUpdateStatus();
AlDone = TRUE;
}
VOID
AlHandleError(
IN AL_ERROR_CONDITION FailingCondition,
IN NET_API_STATUS Status,
IN LPTSTR MessageAlias OPTIONAL
)
/*++
Routine Description:
This routine handles a Alerter service error condition. I*f the error
condition is fatal, then it shuts down the Alerter service.
Arguments:
FailingCondition - Supplies a value which indicates what the failure is.
Status - Supplies the status code for the failure.
MessageAlias - Supplies the message alias name which the alert message
failed in sending. This only applies to the message send error.
Return Value:
None.
--*/
{
LPWSTR SubString[3];
TCHAR StatusString[STRINGS_MAXIMUM + 1];
DWORD NumberOfStrings;
switch (FailingCondition) {
case AlErrorRegisterControlHandler:
NetpKdPrint(("[Alerter] Cannot register control handler "
FORMAT_API_STATUS "\n", Status));
SubString[0] = ultow(Status, StatusString, 10);
AlLogEvent(
NELOG_FailedToRegisterSC,
1,
SubString
);
AlShutdownAlerter(Status);
break;
case AlErrorCreateMailslot:
NetpKdPrint(("[Alerter] Cannot create mailslot " FORMAT_API_STATUS "\n",
Status));
SubString[0] = ultow(Status, StatusString, 10);
AlLogEvent(
NELOG_Mail_Slt_Err,
1,
SubString
);
AlShutdownAlerter(Status);
break;
case AlErrorNotifyServiceController:
NetpKdPrint(("[Alerter] SetServiceStatus error %lu\n", Status));
SubString[0] = ultow(Status, StatusString, 10);
AlLogEvent(
NELOG_FailedToSetServiceStatus,
1,
SubString
);
AlShutdownAlerter(Status);
break;
case AlErrorGetComputerName:
NetpKdPrint(("[Alerter] Error in getting computer name %lu.\n", Status));
SubString[0] = ultow(Status, StatusString, 10);
AlLogEvent(
NELOG_FailedToGetComputerName,
1,
SubString
);
AlShutdownAlerter(Status);
break;
case AlErrorSendMessage :
AlFormatErrorMessage(
Status,
MessageAlias,
StatusString,
(STRINGS_MAXIMUM + 1) * sizeof(TCHAR)
);
SubString[0] = StatusString;
SubString[1] = StatusString + STRLEN(StatusString) + 1;
SubString[2] = SubString[1] + STRLEN(SubString[1]) + 1;
AlLogEvent(
NELOG_Message_Send,
3,
SubString
);
break;
default:
NetpKdPrint(("[Alerter] AlHandleError: unknown error condition %lu\n",
FailingCondition));
NetpAssert(FALSE);
}
}
STATIC
NET_API_STATUS
AlUpdateStatus(
VOID
)
/*++
Routine Description:
This routine updates the Alerter 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 (AlGlobalData.StatusHandle == 0) {
NetpKdPrint((
"[Alerter] Cannot call SetServiceStatus, no status handle.\n"
));
return ERROR_INVALID_HANDLE;
}
if (! SetServiceStatus(AlGlobalData.StatusHandle, &AlGlobalData.Status)) {
status = GetLastError();
IF_DEBUG(MAIN) {
NetpKdPrint(("[Alerter] SetServiceStatus error %lu\n", status));
}
}
return status;
}
VOID
AlerterControlHandler(
IN DWORD Opcode
)
/*++
Routine Description:
This is the service control handler of the Alerter service.
Arguments:
Opcode - Supplies a value which specifies the action for the Alerter
service to perform.
Return Value:
None.
--*/
{
IF_DEBUG(MAIN) {
NetpKdPrint(("[Alerter] In Control Handler\n"));
}
switch (Opcode) {
case SERVICE_CONTROL_STOP:
if (AlGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
IF_DEBUG(MAIN) {
NetpKdPrint(("[Alerter] Stopping alerter...\n"));
}
AlShutdownAlerter(NERR_Success);
}
return;
case SERVICE_CONTROL_INTERROGATE:
break;
default:
IF_DEBUG(MAIN) {
NetpKdPrint(("Unknown alerter opcode " FORMAT_HEX_DWORD
"\n", Opcode));
}
}
//
// Send the status response.
//
(void) AlUpdateStatus();
}