mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
888 lines
25 KiB
888 lines
25 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
status.c
|
|
|
|
Abstract:
|
|
|
|
This file contains functions that are involved with setting the
|
|
status for a service in the service controller.
|
|
|
|
RSetServiceStatus
|
|
Removal Thread
|
|
RI_ScSetServiceBitsA
|
|
RI_ScSetServiceBitsW
|
|
ScRemoveServiceBits
|
|
ScInitServerAnnounceFcn
|
|
|
|
Author:
|
|
|
|
Dan Lafferty (danl) 20-Mar-1991
|
|
|
|
Environment:
|
|
|
|
User Mode -Win32
|
|
|
|
Revision History:
|
|
|
|
11-Apr-1996 anirudhs
|
|
RSetServiceStatus: Notify NDIS when a service that belongs to a
|
|
group NDIS is interested in starts running.
|
|
|
|
21-Nov-1995 anirudhs
|
|
RI_ScSetServiceBitsW: Catch access violations caused if the
|
|
hServiceStatus parameter is invalid.
|
|
|
|
23-Mar-1994 danl
|
|
RSetServiceStatus: Only set the PopupStartFail flag when we have
|
|
actually logged an event. This means that now an auto-started service
|
|
can quietly stop itself without reporting an exit code, and we will not
|
|
log an event or put up a popup.
|
|
However, we will still put up a popup if a service stops itself during
|
|
auto-start, and it provides an exit code.
|
|
|
|
|
|
20-Oct-1993 danl
|
|
RSetServiceStatus: Only update the status if the service process is
|
|
still running. It is possible that the status could have been blocked
|
|
when the process unexpectedly terminated, and updated the status to
|
|
stopped. In this case, the status that was blocked contains
|
|
out-of-date information.
|
|
|
|
10-Dec-1992 danl
|
|
RI_ScSetServiceBitsW & ScRemoveServiceBits no longer hold locks when
|
|
calling ScNetServerSetServiceBits.
|
|
|
|
03-Nov-1992 danl
|
|
RSetServiceStatus: Remove code that sets ExitCode to ERROR_GEN_FAILURE
|
|
when a service transitions directly from START_PENDING to STOPPED with
|
|
out an exit code of its own.
|
|
|
|
25-Aug-1992 danl
|
|
RSetServiceStatus: Allow dirty checkpoint and exitcode fields.
|
|
Force them clean.
|
|
|
|
19-Jun-1991 danl
|
|
Allow ExitCodes to be specified for the SERVICE_STOP_PENDING state.
|
|
Prior to this they were only allowed for the SERVICE_STOP state.
|
|
|
|
20-Mar-1991 danl
|
|
created
|
|
|
|
--*/
|
|
|
|
//
|
|
// INCLUDES
|
|
//
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h> // DbgPrint prototype
|
|
#include <rpc.h> // DataTypes and runtime APIs
|
|
#include <nturtl.h> // needed for winbase.h
|
|
#include <windows.h> // WaitForSingleObject
|
|
|
|
#include <svcctl.h> // MIDL Generated Header File
|
|
#include <tstr.h> // Unicode string macros
|
|
|
|
#include <winsvc.h> // public Service Controller Interface.
|
|
|
|
#include <scdebug.h> // SC_LOG
|
|
#include "dataman.h" // LPIMAGE_RECORD
|
|
#include "scopen.h" // Handle structures and signature definitions
|
|
#include "valid.h" // ScCurrentStateInvalid
|
|
#include "svcctrl.h" // ScRemoveServiceBits
|
|
#include "depend.h" // ScNotifyChangeState
|
|
#include "driver.h" // ScNotifyNdis
|
|
|
|
#include <lmcons.h> // NET_API_STATUS
|
|
#include <lmerr.h> // NERR_Success
|
|
#include <lmsname.h> // contains service name
|
|
#include <lmserver.h> // SV_TYPE_NT (server announcement bits)
|
|
#include <srvann.h> // I_NetServerSetServiceBits
|
|
#include <svcslib.h> // SvcRemoveWorkItem
|
|
|
|
//
|
|
// GLOBALS
|
|
//
|
|
//
|
|
// This is a special storage place for the OR'd server announcement
|
|
// bit masks. NOTE: This is only read or written to when the
|
|
// service database exclusive lock is held.
|
|
//
|
|
DWORD GlobalServerAnnounce = SV_TYPE_NT;
|
|
|
|
|
|
//
|
|
// The following ServerHandle is the handle returned from the
|
|
// LoadLibrary call which loaded netapi.dll. The entrypoint for
|
|
// I_NetServerSetServiceBits is then found and stored in the
|
|
// global location described below.
|
|
//
|
|
HANDLE ScGlobalServerHandle;
|
|
|
|
typedef DWORD (WINAPI *SETSBPROC)();
|
|
|
|
SETSBPROC ScNetServerSetServiceBits = NULL;
|
|
|
|
|
|
//
|
|
// Function Prototypes (local functions)
|
|
//
|
|
|
|
DWORD
|
|
RemovalThread(
|
|
IN LPSERVICE_RECORD ServiceRecord
|
|
);
|
|
|
|
|
|
|
|
DWORD
|
|
RSetServiceStatus(
|
|
IN SERVICE_STATUS_HANDLE hServiceStatus,
|
|
IN LPSERVICE_STATUS lpServiceStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by services when they need to inform the
|
|
service controller of a change in state.
|
|
|
|
Arguments:
|
|
|
|
hServiceStatus - A DWORD value that is actually a pointer to a
|
|
service record in the database. Since this is not a
|
|
handle of SC_HANDLE type, there is no signature to check for
|
|
this handle.
|
|
|
|
lpServiceStatus - A pointer to a SERVICE_STATUS structure. This
|
|
reflects the latest status of the calling service.
|
|
|
|
Return Value:
|
|
|
|
ERROR_INVALID_HANDLE - The specified handle is invalid.
|
|
|
|
ERROR_INVALID_SERVICE_STATUS - The specified service status is invalid.
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
LPSERVICE_RECORD serviceRecord;
|
|
DWORD threadId;
|
|
HANDLE threadHandle;
|
|
DWORD oldState;
|
|
DWORD oldType;
|
|
|
|
LPWSTR ScSubStrings[2];
|
|
WCHAR ScErrorCodeString[25];
|
|
|
|
|
|
SC_LOG(TRACE,"In RSetServiceStatus routine\n",0);
|
|
|
|
if (hServiceStatus == (SERVICE_STATUS_HANDLE) NULL) {
|
|
return(ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
try {
|
|
//
|
|
// Check the signature on the handle.
|
|
//
|
|
if (((LPSERVICE_RECORD)hServiceStatus)->Signature != SERVICE_SIGNATURE) {
|
|
SC_LOG(TRACE,"RSetServiceStatus: hServiceStatus was invalid\n",0);
|
|
status = ERROR_INVALID_HANDLE;
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
status = ERROR_INVALID_HANDLE;
|
|
}
|
|
if (status != NO_ERROR) {
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Validate the fields in the service status structure.
|
|
//
|
|
|
|
if (ScCurrentStateInvalid(lpServiceStatus->dwCurrentState)) {
|
|
SC_LOG2(ERROR, "RSetServiceStatus: " FORMAT_LPWSTR " set invalid "
|
|
" dwCurrentState x%08lx\n",
|
|
((LPSERVICE_RECORD) hServiceStatus)->DisplayName,
|
|
lpServiceStatus->dwCurrentState);
|
|
|
|
ScSubStrings[0] = ((LPSERVICE_RECORD) hServiceStatus)->DisplayName;
|
|
ScSubStrings[1] = ultow(
|
|
lpServiceStatus->dwCurrentState,
|
|
ScErrorCodeString,
|
|
16
|
|
);
|
|
ScLogEvent(
|
|
EVENT_BAD_SERVICE_STATE,
|
|
2,
|
|
ScSubStrings
|
|
);
|
|
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
|
|
if( (SERVICE_STATUS_TYPE_INVALID(lpServiceStatus->dwServiceType)) ||
|
|
(CONTROLS_ACCEPTED_INVALID(lpServiceStatus->dwControlsAccepted)) ) {
|
|
|
|
SC_LOG(ERROR,"RSetServiceStatus: Error in one of the following\n"
|
|
" ServiceType x%08lx\n",
|
|
lpServiceStatus->dwServiceType);
|
|
|
|
SC_LOG(ERROR," ControlsAccepted x%08lx\n",
|
|
lpServiceStatus->dwControlsAccepted);
|
|
|
|
return(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// If the service is not in the stopped or stop-pending state, then the
|
|
// exit code fields should be 0.
|
|
//
|
|
if (((lpServiceStatus->dwCurrentState != SERVICE_STOPPED) &&
|
|
(lpServiceStatus->dwCurrentState != SERVICE_STOP_PENDING))
|
|
|
|
&&
|
|
|
|
((lpServiceStatus->dwWin32ExitCode != 0) ||
|
|
(lpServiceStatus->dwServiceSpecificExitCode != 0)) ){
|
|
|
|
SC_LOG(TRACE,"RSetServiceStatus: ExitCode fields not cleaned up "
|
|
"when state indicates SERVICE_STOPPED\n",0);
|
|
|
|
lpServiceStatus->dwWin32ExitCode = 0;
|
|
lpServiceStatus->dwServiceSpecificExitCode = 0;
|
|
}
|
|
|
|
//
|
|
// If the service is not in a pending state, then the waitHint and
|
|
// checkPoint fields should be 0.
|
|
//
|
|
if ( ( (lpServiceStatus->dwCurrentState == SERVICE_STOPPED) ||
|
|
(lpServiceStatus->dwCurrentState == SERVICE_RUNNING) ||
|
|
(lpServiceStatus->dwCurrentState == SERVICE_PAUSED) )
|
|
&&
|
|
( (lpServiceStatus->dwCheckPoint != 0) ||
|
|
(lpServiceStatus->dwWaitHint != 0) ) ){
|
|
|
|
SC_LOG(TRACE,"RSetServiceStatus: Dirty Checkpoint and WaitHint fields\n",0);
|
|
lpServiceStatus->dwCheckPoint = 0;
|
|
lpServiceStatus->dwWaitHint = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Update the service record. Exclusive locks are required for this.
|
|
//
|
|
// NOTICE that we don't destroy the ServiceType information that was
|
|
// in the service record.
|
|
//
|
|
serviceRecord = (LPSERVICE_RECORD)hServiceStatus;
|
|
|
|
SC_LOG(TRACE,"RSetServiceStatus: Status field accepted, service %ws\n",
|
|
serviceRecord->ServiceName);
|
|
|
|
|
|
ScDatabaseLock( SC_GET_EXCLUSIVE,"RSetServiceStatus1");
|
|
|
|
oldState = serviceRecord->ServiceStatus.dwCurrentState;
|
|
oldType = serviceRecord->ServiceStatus.dwServiceType;
|
|
|
|
//
|
|
// It is possible that while we were blocked waiting for the lock,
|
|
// that a running service could have terminated (Due to the process
|
|
// terminating). So we need to look for "late" status updates, and
|
|
// filter them out. If the ImageRecord pointer is NULL, then the
|
|
// Service has Terminated. Otherwise update the status.
|
|
//
|
|
if (serviceRecord->ImageRecord != NULL) {
|
|
|
|
//
|
|
// Update to the new status
|
|
//
|
|
memcpy(
|
|
&(serviceRecord->ServiceStatus),
|
|
lpServiceStatus,
|
|
sizeof(SERVICE_STATUS));
|
|
|
|
serviceRecord->ServiceStatus.dwServiceType = oldType;
|
|
}
|
|
|
|
//
|
|
// For dependency handling
|
|
//
|
|
if ((serviceRecord->ServiceStatus.dwCurrentState == SERVICE_RUNNING ||
|
|
serviceRecord->ServiceStatus.dwCurrentState == SERVICE_STOPPED ||
|
|
serviceRecord->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) &&
|
|
oldState == SERVICE_START_PENDING) {
|
|
|
|
if (serviceRecord->ServiceStatus.dwCurrentState == SERVICE_STOPPED ||
|
|
serviceRecord->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
|
|
|
|
serviceRecord->StartState = SC_START_FAIL;
|
|
SC_LOG(DEPEND, "%ws START_PENDING -> FAIL\n", serviceRecord->ServiceName);
|
|
|
|
}
|
|
else if (serviceRecord->ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
|
|
serviceRecord->StartState = SC_START_SUCCESS;
|
|
SC_LOG(DEPEND, "%ws START_PENDING -> RUNNING\n", serviceRecord->ServiceName);
|
|
#ifdef TIMING_TEST
|
|
DbgPrint("[SC_TIMING] TickCount for RUNNING service \t%ws\t%d\n",
|
|
serviceRecord->ServiceName, GetTickCount());
|
|
#endif // TIMING_TEST
|
|
}
|
|
|
|
//
|
|
// Tell the dependency handling code that a start-pending
|
|
// service is now running or stopped.
|
|
//
|
|
ScNotifyChangeState();
|
|
}
|
|
|
|
//
|
|
// If the new status indicates that the service has just started,
|
|
// tell NDIS to issue the PNP notifications about this service's arrival,
|
|
// if it belongs to one of the groups NDIS is interested in.
|
|
//
|
|
if ((lpServiceStatus->dwCurrentState == SERVICE_RUNNING) &&
|
|
(oldState != SERVICE_RUNNING)) {
|
|
|
|
ScDatabaseLock(SC_MAKE_SHARED,"RSetServiceStatus1.5");
|
|
ScNotifyNdis(serviceRecord);
|
|
}
|
|
|
|
|
|
ScDatabaseLock(SC_RELEASE,"RSetServiceStatus2");
|
|
|
|
|
|
//
|
|
// If the new status indicates that the service has just stopped,
|
|
// we need to check to see if there are any other services running
|
|
// in the service process. If not, then we can ask the service to
|
|
// terminate. Another thread is spawned to handle this since we need
|
|
// to return from this call in order to allow the service to complete
|
|
// its shutdown.
|
|
//
|
|
|
|
if ((lpServiceStatus->dwCurrentState == SERVICE_STOPPED) &&
|
|
(oldState != SERVICE_STOPPED)) {
|
|
|
|
if (lpServiceStatus->dwWin32ExitCode != NO_ERROR) {
|
|
if (lpServiceStatus->dwWin32ExitCode !=
|
|
ERROR_SERVICE_SPECIFIC_ERROR) {
|
|
|
|
ScSubStrings[0] = serviceRecord->DisplayName;
|
|
wcscpy(ScErrorCodeString,L"%%");
|
|
ultow(lpServiceStatus->dwWin32ExitCode, ScErrorCodeString+2, 10);
|
|
ScSubStrings[1] = ScErrorCodeString;
|
|
ScLogEvent(
|
|
EVENT_SERVICE_EXIT_FAILED,
|
|
2,
|
|
ScSubStrings
|
|
);
|
|
|
|
}
|
|
else {
|
|
ScSubStrings[0] = serviceRecord->DisplayName;
|
|
ScSubStrings[1] = ultow(
|
|
lpServiceStatus->dwServiceSpecificExitCode,
|
|
ScErrorCodeString,
|
|
10
|
|
);
|
|
ScLogEvent(
|
|
EVENT_SERVICE_EXIT_FAILED_SPECIFIC,
|
|
2,
|
|
ScSubStrings
|
|
);
|
|
}
|
|
//
|
|
// For popup after user has logged on to indicate that some service
|
|
// started at boot has failed.
|
|
//
|
|
if (serviceRecord->ErrorControl == SERVICE_ERROR_NORMAL ||
|
|
serviceRecord->ErrorControl == SERVICE_ERROR_SEVERE ||
|
|
serviceRecord->ErrorControl == SERVICE_ERROR_CRITICAL) {
|
|
|
|
ScPopupStartFail = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clear the server announcement bits in the global location
|
|
// for this service.
|
|
//
|
|
ScRemoveServiceBits(serviceRecord);
|
|
|
|
//
|
|
// BUGBUG: If possible, don't do anything with locks here.
|
|
// It would be better if ScRemoveService would get
|
|
// the exclusive lock itself - rather than expect the
|
|
// shared lock and converting it to exclusive.
|
|
// Check into places that call ScRemoveService.
|
|
//
|
|
|
|
ScDatabaseLock( SC_GET_SHARED,"RSetServiceStatus3");
|
|
|
|
//
|
|
// If this is the last service in the process, then delete the
|
|
// process handle from the ProcessWatcher list.
|
|
//
|
|
if ((serviceRecord->ImageRecord != NULL) &&
|
|
(serviceRecord->ImageRecord->ServiceCount == 1)) {
|
|
|
|
if (SvcRemoveWorkItem(serviceRecord->ImageRecord->ObjectWaitHandle)) {
|
|
serviceRecord->ImageRecord->ObjectWaitHandle = NULL;
|
|
}
|
|
}
|
|
|
|
SC_LOG(TRACE,
|
|
"RSetServiceStatus:Create a thread to run ScRemoveService\n",0);
|
|
|
|
threadHandle = CreateThread (
|
|
NULL, // Thread Attributes.
|
|
0L, // Stack Size
|
|
(LPTHREAD_START_ROUTINE)RemovalThread, // lpStartAddress
|
|
(LPVOID)serviceRecord, // lpParameter
|
|
0L, // Creation Flags
|
|
&threadId); // lpThreadId
|
|
|
|
if (threadHandle == (HANDLE) NULL) {
|
|
SC_LOG(ERROR,"RSetServiceStatus:CreateThread failed %d\n",
|
|
GetLastError());
|
|
|
|
//
|
|
// If a thread couldn't be created to remove the service,
|
|
// It is removed in the context of this thread. The
|
|
// result of this is a somewhat dirty termination. The
|
|
// service record will be removed from the installed database.
|
|
// If this was the last service in the process, the process will
|
|
// terminate before we return to the thread.
|
|
//
|
|
|
|
status = ScRemoveService(serviceRecord);
|
|
ScDatabaseLock( SC_RELEASE,"RSetServiceStatus4");
|
|
}
|
|
else {
|
|
//
|
|
// The Thread Creation was successful. Allow that thread
|
|
// to free the lock.
|
|
//
|
|
SC_LOG(TRACE,"Thread Creation Success, thread id = %#lx\n",threadId);
|
|
CloseHandle(threadHandle);
|
|
}
|
|
}
|
|
|
|
SC_LOG(TRACE,"Return from RSetServiceStatus\n",0);
|
|
|
|
return(status);
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
RemovalThread(
|
|
IN LPSERVICE_RECORD ServiceRecord
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This thread is used by NetrServiceStatus to remove a service from the
|
|
Service Controller's database, and also - if necessary - shut down
|
|
the service process. The later step is only done if this was the last
|
|
service running in that process.
|
|
|
|
The use of this thread allows NetServiceStatus to return to the service
|
|
so that the service can then continue to terminate itself.
|
|
|
|
QUESTION:
|
|
Am I going to have synchronization problems with the database
|
|
by passing in a pointer to the ServiceRecord? How should I work
|
|
locks on the database?
|
|
|
|
Arguments:
|
|
|
|
ServiceRecord - This is a pointer to the service record that is being
|
|
removed.
|
|
|
|
Return Value:
|
|
|
|
Same as return values for RemoveService().
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// RemoveService assumes that the shared lock is already obtained.
|
|
//
|
|
|
|
ScRemoveService (ServiceRecord);
|
|
|
|
ScDatabaseLock( SC_RELEASE, "RemovalThread1");
|
|
ExitThread(0);
|
|
return(0);
|
|
}
|
|
|
|
DWORD
|
|
RI_ScSetServiceBitsA(
|
|
IN SERVICE_STATUS_HANDLE hServiceStatus,
|
|
IN DWORD dwServiceBits,
|
|
IN DWORD bSetBitsOn,
|
|
IN DWORD bUpdateImmediately,
|
|
IN LPSTR pszReserved
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function Or's the Service Bits that are passed in - into a
|
|
global bitmask maintained by the service controller. Everytime this
|
|
function is called, we check to see if the server service is running.
|
|
If it is, then we call an internal entry point in the server service
|
|
to pass in the complete bitmask.
|
|
|
|
This function also Or's the Service Bits into the ServerAnnounce
|
|
element in the service's ServiceRecord.
|
|
|
|
NOTE: The exclusive database lock is obtained and held while the
|
|
service record is being read, and while the GlobalServerAnnounce
|
|
bits are set.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was completely successful. The information
|
|
may or may not be delivered to the Server depending on if it is
|
|
running or not.
|
|
|
|
or any error returned from the server service I_NetServerSetServiceBits
|
|
function.
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
|
|
if (ScShutdownInProgress) {
|
|
return(ERROR_SHUTDOWN_IN_PROGRESS);
|
|
}
|
|
|
|
if (pszReserved != NULL) {
|
|
return (ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
return RI_ScSetServiceBitsW(hServiceStatus,
|
|
dwServiceBits,
|
|
bSetBitsOn,
|
|
bUpdateImmediately,
|
|
NULL);
|
|
|
|
}
|
|
|
|
DWORD
|
|
RI_ScSetServiceBitsW(
|
|
IN SERVICE_STATUS_HANDLE hServiceStatus,
|
|
IN DWORD dwServiceBits,
|
|
IN DWORD bSetBitsOn,
|
|
IN DWORD bUpdateImmediately,
|
|
IN LPWSTR pszReserved
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function Or's the Service Bits that are passed in - into a
|
|
global bitmask maintained by the service controller. Everytime this
|
|
function is called, we check to see if the server service is running.
|
|
If it is, then we call an internal entry point in the server service
|
|
to pass in the complete bitmask.
|
|
|
|
This function also Or's the Service Bits into the ServerAnnounce
|
|
element in the service's ServiceRecord.
|
|
|
|
NOTE: The exclusive database lock is obtained and held while the
|
|
service record is being read, and while the GlobalServerAnnounce
|
|
bits are set.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was completely successful.
|
|
|
|
ERROR_GEN_FAILURE - The server service is there, but the call to
|
|
update it failed.
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
LPSERVICE_RECORD serviceRecord;
|
|
LPWSTR serverServiceName;
|
|
DWORD serviceState;
|
|
|
|
if (ScShutdownInProgress) {
|
|
return(ERROR_SHUTDOWN_IN_PROGRESS);
|
|
}
|
|
|
|
if (pszReserved != NULL) {
|
|
return (ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (ScNetServerSetServiceBits == (SETSBPROC)NULL) {
|
|
if (! ScInitServerAnnounceFcn()) {
|
|
return(ERROR_NO_NETWORK);
|
|
}
|
|
}
|
|
|
|
serverServiceName = SERVICE_SERVER;
|
|
|
|
try {
|
|
//
|
|
// Check the signature on the handle.
|
|
//
|
|
if (((LPSERVICE_RECORD)hServiceStatus)->Signature != SERVICE_SIGNATURE) {
|
|
SC_LOG(ERROR,"RI_ScSetServiceBitsW: hServiceStatus %#lx was invalid\n",
|
|
hServiceStatus);
|
|
status = ERROR_INVALID_HANDLE;
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
status = ERROR_INVALID_HANDLE;
|
|
}
|
|
if (status != NO_ERROR) {
|
|
return(status);
|
|
}
|
|
|
|
|
|
ScDatabaseLock(SC_GET_EXCLUSIVE,"ScSetServiceBits1");
|
|
|
|
serviceRecord = (LPSERVICE_RECORD)hServiceStatus;
|
|
|
|
if (bSetBitsOn) {
|
|
//
|
|
// Set the bits in the global location.
|
|
//
|
|
GlobalServerAnnounce |= dwServiceBits;
|
|
|
|
//
|
|
// Set the bits in the service record.
|
|
//
|
|
|
|
serviceRecord->ServerAnnounce |= dwServiceBits;
|
|
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Clear the bits in the global location.
|
|
//
|
|
GlobalServerAnnounce &= ~dwServiceBits;
|
|
|
|
//
|
|
// Clear the bits in the service record.
|
|
//
|
|
|
|
serviceRecord->ServerAnnounce &= ~dwServiceBits;
|
|
|
|
|
|
}
|
|
//
|
|
// If the server service is running, then send the Global mask to
|
|
// the server service.
|
|
//
|
|
|
|
status = ScGetNamedServiceRecord(
|
|
serverServiceName,
|
|
&serviceRecord);
|
|
|
|
if (status == NO_ERROR) {
|
|
|
|
serviceState = serviceRecord->ServiceStatus.dwCurrentState;
|
|
ScDatabaseLock(SC_RELEASE,"ScSetServiceBits2");
|
|
|
|
if (serviceState == SERVICE_RUNNING) {
|
|
|
|
status = ScNetServerSetServiceBits(
|
|
NULL, // ServerName
|
|
NULL, // TransportName
|
|
GlobalServerAnnounce,
|
|
bUpdateImmediately);
|
|
|
|
if (status != NERR_Success) {
|
|
SC_LOG(ERROR,"I_ScSetServiceBits: I_NetServerSetServiceBits failed %lu\n",
|
|
status);
|
|
}
|
|
else {
|
|
SC_LOG(TRACE,"I_ScSetServiceBits: I_NetServerSetServiceBits success\n",0);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ScDatabaseLock(SC_RELEASE,"ScSetServiceBits3");
|
|
status = NO_ERROR;
|
|
}
|
|
|
|
SC_LOG(TRACE,"I_ScSetServiceBits: GlobalServerAnnounce = 0x%lx\n",
|
|
GlobalServerAnnounce);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScRemoveServiceBits(
|
|
IN LPSERVICE_RECORD ServiceRecord
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called when a service stops running. It looks in
|
|
the service record for any server announcement bits that are set
|
|
and turns them off in GlobalServerAnnounce. The ServerAnnounce
|
|
element in the service record is set to 0.
|
|
|
|
Arguments:
|
|
|
|
ServiceRecord - This is a pointer to the service record that
|
|
has changed to the stopped state.
|
|
|
|
Return Value:
|
|
|
|
The status returned from I_NetServerSetServiceBits.
|
|
|
|
--*/
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
LPSERVICE_RECORD serverServiceRecord;
|
|
DWORD serviceState;
|
|
|
|
|
|
if (ScNetServerSetServiceBits == (SETSBPROC)NULL) {
|
|
if (! ScInitServerAnnounceFcn()) {
|
|
return(ERROR_NO_NETWORK);
|
|
}
|
|
}
|
|
|
|
if (ServiceRecord->ServerAnnounce != 0) {
|
|
|
|
ScDatabaseLock(SC_GET_EXCLUSIVE,"ScRemoveServiceBits1");
|
|
|
|
//
|
|
// Clear the bits in the global location.
|
|
//
|
|
GlobalServerAnnounce &= ~(ServiceRecord->ServerAnnounce);
|
|
|
|
|
|
//
|
|
// Clear the bits in the service record.
|
|
//
|
|
|
|
ServiceRecord->ServerAnnounce = 0;
|
|
|
|
SC_LOG1(TRACE,"RemoveServiceBits: New GlobalServerAnnounce = 0x%lx\n",
|
|
GlobalServerAnnounce);
|
|
|
|
//
|
|
// If the server service is running, then send the Global mask to
|
|
// the server service.
|
|
//
|
|
|
|
status = ScGetNamedServiceRecord(
|
|
SERVICE_SERVER,
|
|
&serverServiceRecord);
|
|
|
|
|
|
if (status == NO_ERROR) {
|
|
|
|
serviceState = serverServiceRecord->ServiceStatus.dwCurrentState;
|
|
ScDatabaseLock(SC_RELEASE,"ScRemoveServiceBits2");
|
|
|
|
if ( serviceState == SERVICE_RUNNING) {
|
|
|
|
status = ScNetServerSetServiceBits(
|
|
NULL, // ServerName
|
|
NULL, // Transport name
|
|
GlobalServerAnnounce,
|
|
TRUE); // Update immediately.
|
|
|
|
if (status != NERR_Success) {
|
|
SC_LOG(ERROR,"ScRemoveServiceBits: I_NetServerSetServiceBits failed %d\n",
|
|
status);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ScDatabaseLock(SC_RELEASE,"ScRemoveServiceBits2");
|
|
}
|
|
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
BOOL
|
|
ScInitServerAnnounceFcn(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
ScGlobalServerHandle = LoadLibraryW(L"netapi32.dll");
|
|
|
|
if (ScGlobalServerHandle == NULL) {
|
|
SC_LOG(ERROR,"ScInitServerAnnouncFcn: LoadLibrary failed %d\n",
|
|
GetLastError());
|
|
return(FALSE);
|
|
}
|
|
|
|
ScNetServerSetServiceBits = (SETSBPROC)GetProcAddress(
|
|
ScGlobalServerHandle,
|
|
"I_NetServerSetServiceBits");
|
|
|
|
|
|
if (ScNetServerSetServiceBits == (SETSBPROC)NULL) {
|
|
SC_LOG(ERROR,"ScInitServerAnnouncFcn: GetProcAddress failed %d\n",
|
|
GetLastError());
|
|
return(FALSE);
|
|
}
|
|
return TRUE;
|
|
}
|