2328 lines
55 KiB
C++
2328 lines
55 KiB
C++
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
groupman.cxx
|
||
|
||
Abstract:
|
||
|
||
Contains code for the Group List Database manager. This includes
|
||
all the linked list routines. This file contains the following
|
||
functions:
|
||
ScGetOrderGroupList
|
||
ScGetStandaloneGroupList
|
||
ScGetUnresolvedDependList
|
||
ScGetNamedGroupRecord
|
||
|
||
ScCreateOrderGroupEntry
|
||
ScAllocateGroupEntry
|
||
ScCreateGroupMembership
|
||
ScDeleteGroupMembership
|
||
ScCreateRegistryGroupPointer
|
||
ScDeleteRegistryGroupPointer
|
||
ScCreateStandaloneGroup
|
||
ScDeleteStandaloneGroup
|
||
|
||
ScGenerateDependencies
|
||
ScSetDependencyPointers
|
||
ScResolveDependencyToService
|
||
ScCreateDependencies
|
||
ScCreateUnresolvedDepend
|
||
ScDeleteUnresolvedDepend
|
||
ScCreateDependRecord
|
||
ScDeleteStartDependencies
|
||
ScDeleteStopDependencies
|
||
ScSetServiceDependList
|
||
ScGetUniqueTag
|
||
ScCompareVector
|
||
|
||
ScGetDependencySize
|
||
ScGetDependencyString
|
||
|
||
ScDumpGroups
|
||
ScDumpServiceDependencies
|
||
|
||
Author:
|
||
|
||
Dan Lafferty (danl) 04-Feb-1992
|
||
|
||
Environment:
|
||
|
||
User Mode -Win32
|
||
|
||
Revision History:
|
||
|
||
22-Oct-1993 danl
|
||
Created by splitting these functions out of dataman.c because it was
|
||
getting too large.
|
||
|
||
--*/
|
||
|
||
//
|
||
// INCLUDES
|
||
//
|
||
|
||
#include "precomp.hxx"
|
||
#include <stdlib.h> // wide character c runtimes.
|
||
#include <tstr.h> // Unicode string macros
|
||
#include <ntrpcp.h> // MIDL_user_allocate
|
||
#include <control.h> // SendControl
|
||
#include "scconfig.h" // ScGenerateServiceDB,ScInitSecurityProcess
|
||
#include "scsec.h" // ScCreateScServiceObject
|
||
#include "account.h" // ScRemoveAccount
|
||
#include <sclib.h> // ScImagePathsMatch().
|
||
#include "bootcfg.h" // ScDeleteRegTree().
|
||
#include <strarray.h> // ScWStrArraySize
|
||
|
||
//
|
||
// Defines
|
||
//
|
||
|
||
// Names of specially treated groups
|
||
#define SC_GROUPNAME_TDI L"TDI"
|
||
#define SC_GROUPNAME_PNP_TDI L"PNP_TDI"
|
||
|
||
// A value that will not match any real pointer to a load order group
|
||
#define SC_INVALID_GROUP ((LPLOAD_ORDER_GROUP)(DWORD_PTR) 0xFFFFFFFF)
|
||
|
||
|
||
//
|
||
// External Globals
|
||
//
|
||
|
||
//
|
||
// TDI GROUP SPECIAL: The groups named TDI and PNP_TDI are treated
|
||
// specially during dependency handling. This is done by remembering a
|
||
// pointer to each of those groups, if it occurs in the group order list,
|
||
// and checking against the remembered pointers during dependency
|
||
// handling.
|
||
//
|
||
|
||
LPLOAD_ORDER_GROUP ScGlobalTDIGroup = SC_INVALID_GROUP;
|
||
LPLOAD_ORDER_GROUP ScGlobalPNP_TDIGroup = SC_INVALID_GROUP;
|
||
|
||
//
|
||
// Static Globals
|
||
//
|
||
|
||
//
|
||
// These are the linked list heads for each of the databases
|
||
// that are maintained.
|
||
//
|
||
|
||
LOAD_ORDER_GROUP OrderGroupList; // empty header for doubly linked
|
||
LOAD_ORDER_GROUP StandaloneGroupList; // empty header for doubly linked
|
||
|
||
UNRESOLVED_DEPEND UnresolvedDependList; // empty header for doubly linked
|
||
|
||
//
|
||
// Local Function Prototypes
|
||
//
|
||
|
||
DWORD
|
||
ScAllocateOrderGroupEntry(
|
||
OUT LPLOAD_ORDER_GROUP *NewGroup,
|
||
IN LPWSTR GroupName
|
||
);
|
||
|
||
DWORD
|
||
ScCreateStandaloneGroup(
|
||
IN LPWSTR GroupName,
|
||
OUT LPLOAD_ORDER_GROUP *GroupPointer
|
||
);
|
||
|
||
VOID
|
||
ScDeleteStandaloneGroup(
|
||
IN LPLOAD_ORDER_GROUP Group
|
||
);
|
||
|
||
VOID
|
||
ScRememberSpecialGroup(
|
||
IN LPLOAD_ORDER_GROUP Group
|
||
);
|
||
|
||
VOID
|
||
ScForgetSpecialGroup(
|
||
IN LPLOAD_ORDER_GROUP Group
|
||
);
|
||
|
||
DWORD
|
||
ScCreateUnresolvedDepend(
|
||
IN LPWSTR Name,
|
||
OUT LPUNRESOLVED_DEPEND *Unresolved
|
||
);
|
||
|
||
VOID
|
||
ScDeleteUnresolvedDepend(
|
||
IN OUT LPUNRESOLVED_DEPEND *Unresolved
|
||
);
|
||
|
||
DWORD
|
||
ScSetServiceDependList(
|
||
LPDEPEND_RECORD Start,
|
||
LPSERVICE_RECORD ServiceRecord,
|
||
PVOID DependOnRecord,
|
||
DEPEND_TYPE DependOnType
|
||
);
|
||
|
||
VOID
|
||
ScCompareVector(
|
||
IN LPDWORD TagArray,
|
||
IN DWORD TagArrayLength,
|
||
IN OUT LPDWORD ReturnTagPtr
|
||
);
|
||
|
||
|
||
|
||
//****************************************************************************/
|
||
// Miscellaneous Short Functions
|
||
//****************************************************************************/
|
||
LPLOAD_ORDER_GROUP
|
||
ScGetOrderGroupList(
|
||
VOID
|
||
)
|
||
{
|
||
SC_ASSERT(ScGroupListLock.Have());
|
||
return OrderGroupList.Next;
|
||
}
|
||
|
||
LPLOAD_ORDER_GROUP
|
||
ScGetStandaloneGroupList(
|
||
VOID
|
||
)
|
||
{
|
||
SC_ASSERT(ScGroupListLock.Have());
|
||
return StandaloneGroupList.Next;
|
||
}
|
||
|
||
LPUNRESOLVED_DEPEND
|
||
ScGetUnresolvedDependList(
|
||
VOID
|
||
)
|
||
{
|
||
SC_ASSERT(ScGroupListLock.Have());
|
||
return UnresolvedDependList.Next;
|
||
}
|
||
|
||
VOID
|
||
ScInitGroupDatabase(VOID)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
OrderGroupList.Next = NULL;
|
||
OrderGroupList.Prev = NULL;
|
||
|
||
StandaloneGroupList.Next = NULL;
|
||
StandaloneGroupList.Prev = NULL;
|
||
|
||
UnresolvedDependList.Next = NULL;
|
||
UnresolvedDependList.Prev = NULL;
|
||
|
||
}
|
||
|
||
VOID
|
||
ScEndGroupDatabase(VOID)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
LPLOAD_ORDER_GROUP Group;
|
||
LPLOAD_ORDER_GROUP Grp;
|
||
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
|
||
Group = OrderGroupList.Next;
|
||
|
||
while (Group != NULL) {
|
||
|
||
Grp = Group;
|
||
Group = Group->Next;
|
||
|
||
REMOVE_FROM_LIST(Grp);
|
||
LocalFree(Grp);
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScCreateOrderGroupEntry(
|
||
IN LPWSTR GroupName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function adds a group entry into the end of the load order group
|
||
list.
|
||
|
||
Arguments:
|
||
|
||
GroupName - Supplies the name of the load group.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR - The operation was successful.
|
||
|
||
ERROR_NOT_ENOUGH_MEMORY - The call to allocate memory for a new
|
||
group entry failed.
|
||
|
||
Note:
|
||
|
||
The GroupListLock must be held exclusively prior to calling this function.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
LPLOAD_ORDER_GROUP NewGroup;
|
||
LPLOAD_ORDER_GROUP GroupListPointer;
|
||
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
|
||
if ((status = ScAllocateOrderGroupEntry(
|
||
&NewGroup,
|
||
GroupName
|
||
)) != NO_ERROR) {
|
||
return status;
|
||
}
|
||
|
||
GroupListPointer = &OrderGroupList;
|
||
|
||
//
|
||
// Add the group entry to the group list at the end.
|
||
//
|
||
ADD_TO_LIST(GroupListPointer, NewGroup);
|
||
|
||
SC_LOG(CONFIG, "ScCreateOrderGroupEntry: Added %ws to GroupList\n", GroupName);
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScAllocateOrderGroupEntry(
|
||
OUT LPLOAD_ORDER_GROUP *NewGroup,
|
||
IN LPWSTR GroupName
|
||
)
|
||
{
|
||
|
||
//
|
||
// Allocate memory for the new group.
|
||
//
|
||
*NewGroup = (LPLOAD_ORDER_GROUP)LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
WCSSIZE(GroupName) + sizeof(LOAD_ORDER_GROUP)
|
||
);
|
||
|
||
if (*NewGroup == NULL) {
|
||
SC_LOG(ERROR,"ScAllocateOrderGroupEntry: LocalAlloc failure %ld\n",
|
||
GetLastError());
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
//
|
||
// Save away the GroupName
|
||
//
|
||
(*NewGroup)->GroupName = (LPWSTR) ((LPBYTE) (*NewGroup) + sizeof(LOAD_ORDER_GROUP));
|
||
wcscpy((*NewGroup)->GroupName, GroupName);
|
||
|
||
ScRememberSpecialGroup(*NewGroup);
|
||
|
||
//
|
||
// Set the RefCount field to 0xffffffff so that we can differentiate an
|
||
// order group from a standalone group. This field actually indicates
|
||
// the number of members in a group and dependency references to it if
|
||
// the group is standalone so that we can delete the standalone group
|
||
// when it goes to 0.
|
||
//
|
||
(*NewGroup)->RefCount = MAXULONG;
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScCreateGroupMembership(
|
||
OUT PSERVICE_RECORD ServiceRecord,
|
||
IN LPWSTR Group OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function assigns the load order group membership information
|
||
of the service to its specified service record. If the service
|
||
belongs to a group in OrderGroupList, a pointer to the group in the load
|
||
order group list is saved. If the service belongs to a group which
|
||
is not in the load order group list, the name of the group is saved
|
||
in the service record in case the group gets added to the load order
|
||
group list later. If Group is not specified, no group membership
|
||
information is saved.
|
||
|
||
Arguments:
|
||
|
||
ServiceRecord - Receives the group membership information in this service
|
||
record.
|
||
|
||
Group - Supplies the string which contains the name of the group. This
|
||
is the raw string read from the registry which may contain blank,
|
||
tab or newline characters which we should ignore.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR - The operation was successful.
|
||
|
||
ERROR_NOT_ENOUGH_MEMORY - The call to allocate memory for a group name
|
||
failed.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the database lock is already held. It is
|
||
called by ScAddConfigInfoServiceRecord.
|
||
|
||
It also assumes that the caller has exclusive access to the group
|
||
list lock.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
LPWSTR GroupPtr = Group;
|
||
LPWSTR GroupName;
|
||
PLOAD_ORDER_GROUP GroupEntry = ScGetOrderGroupList();
|
||
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
//
|
||
// Extract the group name from the string read in from the registry.
|
||
//
|
||
if ((! ARGUMENT_PRESENT(GroupPtr)) || (! ScGetToken(&GroupPtr, &GroupName))) {
|
||
ServiceRecord->MemberOfGroup = (PLOAD_ORDER_GROUP) NULL;
|
||
return NO_ERROR;
|
||
}
|
||
|
||
//
|
||
// Search for matching group name in load order list
|
||
//
|
||
while (GroupEntry != NULL) {
|
||
if (_wcsicmp(GroupEntry->GroupName, GroupName) == 0) {
|
||
ServiceRecord->MemberOfGroup = GroupEntry;
|
||
return NO_ERROR;
|
||
}
|
||
GroupEntry = GroupEntry->Next;
|
||
}
|
||
|
||
//
|
||
// Group name not NULL, and not found in load order group list.
|
||
// Group is a standalone group.
|
||
//
|
||
status = ScCreateStandaloneGroup(
|
||
GroupName,
|
||
&(ServiceRecord->MemberOfGroup)
|
||
);
|
||
|
||
if (status != NO_ERROR) {
|
||
return status;
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
VOID
|
||
ScDeleteGroupMembership(
|
||
IN OUT PSERVICE_RECORD ServiceRecord
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deletes any memory allocated for group membership
|
||
association.
|
||
|
||
Arguments:
|
||
|
||
ServiceRecord - Supplies the group membership information in this
|
||
service record.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the database lock is already held. It is
|
||
called by ScAddConfigInfoServiceRecord and ScDecrementUseCountAndDelete.
|
||
|
||
It also assumes that the group list lock is held exclusively.
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
if (ServiceRecord->MemberOfGroup != NULL &&
|
||
ServiceRecord->MemberOfGroup->RefCount != MAXULONG) {
|
||
ScDeleteStandaloneGroup(ServiceRecord->MemberOfGroup);
|
||
}
|
||
|
||
ServiceRecord->MemberOfGroup = NULL;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScCreateRegistryGroupPointer(
|
||
OUT PSERVICE_RECORD ServiceRecord,
|
||
IN LPWSTR Group OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function assigns the load order group RegistryGroup
|
||
information in the service record to match the load order group
|
||
stored in the registry, which is not the same as MemberOfGroup
|
||
information if the load order group of the service is changed
|
||
while the service is running. However, we need to know what the
|
||
resultant load order group of the service is when it stops so
|
||
that when we can guarantee uniqueness of a tag based on all
|
||
members the group.
|
||
|
||
This function does exactly the same thing as the
|
||
ScCreateGroupMembership function but alters the RegistryGroup
|
||
pointer instead of the MemberOfGroup pointer in the service
|
||
record.
|
||
|
||
Arguments:
|
||
|
||
ServiceRecord - Receives the group membership information in this service
|
||
record.
|
||
|
||
Group - Supplies the string which contains the name of the group. This
|
||
is the raw string read from the registry which may contain blank,
|
||
tab or newline characters which we should ignore.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR - The operation was successful.
|
||
|
||
ERROR_NOT_ENOUGH_MEMORY - The call to allocate memory for a group name
|
||
failed.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the database lock is already held. It is
|
||
called by ScAddConfigInfoServiceRecord.
|
||
|
||
It also assumes that the caller has exclusive access to the group
|
||
list lock.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
LPWSTR GroupPtr = Group;
|
||
LPWSTR GroupName;
|
||
PLOAD_ORDER_GROUP GroupEntry = ScGetOrderGroupList();
|
||
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
|
||
//
|
||
// Extract the group name from the string read in from the registry.
|
||
//
|
||
if ((! ARGUMENT_PRESENT(GroupPtr)) || (! ScGetToken(&GroupPtr, &GroupName))) {
|
||
ServiceRecord->RegistryGroup = (PLOAD_ORDER_GROUP) NULL;
|
||
return NO_ERROR;
|
||
}
|
||
|
||
//
|
||
// Search for matching group name in load order list
|
||
//
|
||
while (GroupEntry != NULL) {
|
||
if (_wcsicmp(GroupEntry->GroupName, GroupName) == 0) {
|
||
ServiceRecord->RegistryGroup = GroupEntry;
|
||
return NO_ERROR;
|
||
}
|
||
GroupEntry = GroupEntry->Next;
|
||
}
|
||
|
||
//
|
||
// Group name not NULL, and not found in load order group list.
|
||
// Group is a standalone group.
|
||
//
|
||
status = ScCreateStandaloneGroup(
|
||
GroupName,
|
||
&(ServiceRecord->RegistryGroup)
|
||
);
|
||
|
||
if (status != NO_ERROR) {
|
||
return status;
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
VOID
|
||
ScDeleteRegistryGroupPointer(
|
||
IN OUT PSERVICE_RECORD ServiceRecord
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deletes any memory allocated for registry group
|
||
association.
|
||
|
||
Arguments:
|
||
|
||
ServiceRecord - Supplies the registry group information in this
|
||
service record.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the database lock is already held. It is
|
||
called by ScAddConfigInfoServiceRecord and ScDecrementUseCountAndDelete.
|
||
|
||
It also assumes that the group list lock is held exclusively.
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
if (ServiceRecord->RegistryGroup != NULL &&
|
||
ServiceRecord->RegistryGroup->RefCount != MAXULONG) {
|
||
ScDeleteStandaloneGroup(ServiceRecord->RegistryGroup);
|
||
}
|
||
|
||
ServiceRecord->RegistryGroup = NULL;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScCreateStandaloneGroup(
|
||
IN LPWSTR GroupName,
|
||
OUT LPLOAD_ORDER_GROUP *GroupPointer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function looks for a matching standalone group entry in the
|
||
standalone group list. If a match is found, the reference count is
|
||
incremented and the pointer to the matching entry is returned.
|
||
|
||
If no matching entry is found, this function creates a new standalone
|
||
group entry, insert it into the end of the standalone group list, and
|
||
return a pointer to the new entry.
|
||
|
||
|
||
Arguments:
|
||
|
||
Name - Supplies the name of the group which is not in the
|
||
ServiceOrderList.
|
||
|
||
GroupPointer - Receives a pointer to the unresolved entry.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR - The operation was successful.
|
||
|
||
ERROR_NOT_ENOUGH_MEMORY - Allocation of memory failed.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the caller has exclusively acquired the
|
||
group list lock. It is called by ScCreateGroupMembership.
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
|
||
LPLOAD_ORDER_GROUP Group = ScGetStandaloneGroupList();
|
||
|
||
BOOL Found = FALSE;
|
||
|
||
|
||
//
|
||
// Search the existing standalone group list for the matching
|
||
// standalone group entry.
|
||
//
|
||
while (Group != NULL) {
|
||
|
||
if (_wcsicmp(Group->GroupName, GroupName) == 0) {
|
||
Found = TRUE;
|
||
break;
|
||
}
|
||
|
||
Group = Group->Next;
|
||
}
|
||
|
||
if (Found) {
|
||
|
||
Group->RefCount++;
|
||
|
||
SC_LOG2(DEPEND_DUMP,
|
||
"Found existing group entry for " FORMAT_LPWSTR
|
||
", just increment refcount to %lu\n", Group->GroupName,
|
||
Group->RefCount);
|
||
|
||
*GroupPointer = Group;
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
//
|
||
// Not found. Allocate a new group entry.
|
||
//
|
||
if ((*GroupPointer = (LPLOAD_ORDER_GROUP)LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
sizeof(LOAD_ORDER_GROUP) + WCSSIZE(GroupName)
|
||
)) == NULL) {
|
||
SC_LOG(ERROR,"ScCreateStandaloneGroup: LocalAlloc failure %lu\n",
|
||
GetLastError());
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
(*GroupPointer)->GroupName = (LPWSTR) ((DWORD_PTR) *GroupPointer +
|
||
sizeof(LOAD_ORDER_GROUP));
|
||
wcscpy((*GroupPointer)->GroupName, GroupName);
|
||
|
||
(*GroupPointer)->RefCount = 1;
|
||
|
||
SC_LOG1(DEPEND_DUMP, "Created new standalone group entry "
|
||
FORMAT_LPWSTR "\n", (*GroupPointer)->GroupName);
|
||
|
||
ScRememberSpecialGroup(*GroupPointer);
|
||
|
||
Group = &StandaloneGroupList;
|
||
|
||
//
|
||
// Add the new group entry to the standalone group list at the end.
|
||
//
|
||
ADD_TO_LIST(Group, *GroupPointer);
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
VOID
|
||
ScDeleteStandaloneGroup(
|
||
IN LPLOAD_ORDER_GROUP Group
|
||
)
|
||
{
|
||
if (Group->RefCount) {
|
||
Group->RefCount--;
|
||
|
||
SC_LOG1(DEPEND, "DeleteStandaloneGroup: Subtracted RefCount is "
|
||
FORMAT_DWORD "\n", Group->RefCount);
|
||
}
|
||
else {
|
||
SC_LOG0(ERROR, "ScDeleteStandaloneGroup: Before delete, refcount is 0!\n");
|
||
SC_ASSERT(FALSE);
|
||
}
|
||
|
||
if (Group->RefCount == 0) {
|
||
|
||
SC_LOG1(DEPEND, "Deleting standalone group " FORMAT_LPWSTR "\n",
|
||
Group->GroupName);
|
||
|
||
REMOVE_FROM_LIST(Group);
|
||
|
||
ScForgetSpecialGroup(Group);
|
||
|
||
LocalFree(Group);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
ScRememberSpecialGroup(
|
||
IN LPLOAD_ORDER_GROUP Group
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Compares the group name against a set of known group names to see if it
|
||
is a group that requires special handling, and if so, saves the pointer
|
||
to the group in a global variable.
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
|
||
// CODEWORK: If the number of special groups keeps growing, do this
|
||
// in a table-driven way!
|
||
|
||
if (_wcsicmp(Group->GroupName, SC_GROUPNAME_TDI) == 0)
|
||
{
|
||
if (ScGlobalTDIGroup != SC_INVALID_GROUP)
|
||
{
|
||
SC_LOG0(ERROR, "Warning: TDI group occurs more than once in load order group list\n");
|
||
}
|
||
ScGlobalTDIGroup = Group;
|
||
}
|
||
else if (_wcsicmp(Group->GroupName, SC_GROUPNAME_PNP_TDI) == 0)
|
||
{
|
||
if (ScGlobalPNP_TDIGroup != SC_INVALID_GROUP)
|
||
{
|
||
SC_LOG0(ERROR, "Warning: PNP_TDI group occurs more than once in load order group list\n");
|
||
}
|
||
ScGlobalPNP_TDIGroup = Group;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
ScForgetSpecialGroup(
|
||
IN LPLOAD_ORDER_GROUP Group
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
|
||
if (Group == ScGlobalTDIGroup)
|
||
{
|
||
ScGlobalTDIGroup = SC_INVALID_GROUP;
|
||
}
|
||
else if (Group == ScGlobalPNP_TDIGroup)
|
||
{
|
||
ScGlobalPNP_TDIGroup = SC_INVALID_GROUP;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
ScGenerateDependencies(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
Note:
|
||
|
||
The GroupListLock must be held exclusively prior to calling this routine.
|
||
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
|
||
FOR_ALL_SERVICES(Service)
|
||
{
|
||
(void) ScSetDependencyPointers(Service);
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScSetDependencyPointers(
|
||
IN LPSERVICE_RECORD Service
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
Note:
|
||
|
||
The GroupListLock must be held exclusively prior to calling this routine.
|
||
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
|
||
DWORD status;
|
||
|
||
if (Service->Dependencies != NULL) {
|
||
|
||
status = ScCreateDependencies(
|
||
Service,
|
||
Service->Dependencies
|
||
);
|
||
|
||
if (status == NO_ERROR) {
|
||
LocalFree(Service->Dependencies);
|
||
Service->Dependencies = NULL;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScResolveDependencyToService(
|
||
LPSERVICE_RECORD DependOnService
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function resolves all dependencies to the service we are
|
||
currently installing via CreateService. The start depend entry
|
||
of these services will point to the service record of the service
|
||
we are installing instead of the unresolved depend record. A
|
||
stop depend entry is created for the service we are installing to
|
||
point back to every service that depends on it.
|
||
|
||
Arguments:
|
||
|
||
DependOnService - Supplies a pointer to the service we are installing
|
||
via CreateService which other services may depend on.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR - The operation was successful.
|
||
|
||
ERROR_NOT_ENOUGH_MEMORY - Fail to allocate memory for required data
|
||
structures.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the caller has exclusively acquired the
|
||
database lock. It is called by RCreateServiceW.
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScGroupListLock.Have());
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
DWORD status;
|
||
LPUNRESOLVED_DEPEND UnresolvedEntry = ScGetUnresolvedDependList();
|
||
|
||
|
||
//
|
||
// Search the unresolved depend list for a matching entry
|
||
//
|
||
while (UnresolvedEntry != NULL) {
|
||
|
||
if (_wcsicmp(UnresolvedEntry->Name, DependOnService->ServiceName) == 0) {
|
||
SC_LOG1(DEPEND, "Found unresolved entry for " FORMAT_LPWSTR "\n",
|
||
DependOnService->ServiceName);
|
||
break;
|
||
}
|
||
|
||
UnresolvedEntry = UnresolvedEntry->Next;
|
||
}
|
||
|
||
if (UnresolvedEntry == NULL) {
|
||
//
|
||
// There are no service which depends on the service we are
|
||
// installing; hence, no unresolved dependency to resolve.
|
||
//
|
||
SC_LOG1(DEPEND, "No service depends on " FORMAT_LPWSTR "\n",
|
||
DependOnService->ServiceName);
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
//
|
||
// Loop through all services to see if any of them has a start depend
|
||
// entry that points to UnresolvedEntry.
|
||
//
|
||
FOR_ALL_SERVICES(Service)
|
||
{
|
||
if (UnresolvedEntry == NULL)
|
||
{
|
||
break;
|
||
}
|
||
|
||
for (LPDEPEND_RECORD Start = Service->StartDepend;
|
||
Start != NULL;
|
||
Start = Start->Next)
|
||
{
|
||
if (Start->DependUnresolved == UnresolvedEntry)
|
||
{
|
||
status = ScSetServiceDependList(
|
||
Start,
|
||
Service,
|
||
(PVOID)DependOnService,
|
||
TypeDependOnService
|
||
);
|
||
|
||
if (status != NO_ERROR)
|
||
{
|
||
//
|
||
// Error with creating the stop depend entry for
|
||
// DependOnService. Back out changes set for the
|
||
// current start depend entry.
|
||
//
|
||
Start->DependType = TypeDependOnUnresolved;
|
||
Start->DependUnresolved = UnresolvedEntry;
|
||
|
||
//
|
||
// Back out of all other resolved dependencies to
|
||
// DependOnService will be done in ScDecrementUseCountAndDelete.
|
||
//
|
||
SC_LOG2(ERROR, "ScResolvedDependencyToService " FORMAT_LPWSTR
|
||
" failed " FORMAT_DWORD "\n",
|
||
DependOnService->ServiceName, status);
|
||
return status;
|
||
}
|
||
|
||
SC_LOG2(DEPEND, FORMAT_LPWSTR " depends on " FORMAT_LPWSTR
|
||
". Dependency resolved!\n", Service->ServiceName,
|
||
UnresolvedEntry->Name);
|
||
|
||
ScDeleteUnresolvedDepend(&UnresolvedEntry);
|
||
}
|
||
}
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScCreateDependencies(
|
||
OUT PSERVICE_RECORD ServiceRecord,
|
||
IN LPWSTR Dependencies OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function creates the start dependencies list of a service.
|
||
If the service specified by ServiceRecord depends on a service that
|
||
has not been inserted into the service list yet, that service entry
|
||
will be created and inserted into the service list so that the depend
|
||
record can point to it. The service this one points to in its start
|
||
dependency list will get a new entry in its stop dependency list because
|
||
this one must be stopped before it can stop.
|
||
|
||
The dependencies list is not ordered.
|
||
|
||
NOTE: This function is for call from RChangeServiceConfig.
|
||
|
||
Arguments:
|
||
|
||
ServiceRecord - Receives the start dependencies information in this
|
||
service record.
|
||
|
||
Dependencies - Supplies the string which contains the names this service
|
||
depend on to be started first. This is a multi-sz string of
|
||
service or group names.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR - The operation was successful.
|
||
|
||
ERROR_NOT_ENOUGH_MEMORY - Fail to allocate memory for required data
|
||
structures.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the caller has exclusively acquired the
|
||
database lock. It is called by ScSetDependencyPointers.
|
||
|
||
It also assumes that the caller has exclusively acquired the group
|
||
list lock.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
|
||
LPWSTR DependPtr = Dependencies;
|
||
LPWSTR DependOnName;
|
||
PVOID DependOnRecord = NULL;
|
||
DEPEND_TYPE Type;
|
||
|
||
PDEPEND_RECORD Start;
|
||
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
if (! ARGUMENT_PRESENT(DependPtr)) {
|
||
return NO_ERROR;
|
||
}
|
||
|
||
while (*DependPtr != 0) {
|
||
|
||
if (ScGetToken(&DependPtr, &DependOnName)) {
|
||
|
||
//
|
||
// Initialize flag for noting that a new service record is
|
||
// created for resolving the dependency chain.
|
||
//
|
||
Type = TypeNone;
|
||
|
||
if (*DependOnName != SC_GROUP_IDENTIFIERW) {
|
||
|
||
//
|
||
// Depend on a service
|
||
//
|
||
|
||
//
|
||
// Look for the service we depend on in the service record list
|
||
//
|
||
status = ScGetNamedServiceRecord(
|
||
DependOnName,
|
||
(LPSERVICE_RECORD *) &DependOnRecord
|
||
);
|
||
|
||
if (status == ERROR_SERVICE_DOES_NOT_EXIST) {
|
||
|
||
//
|
||
// Could not find the service we depend on. Create an
|
||
// unresolved dependency entry.
|
||
//
|
||
status = ScCreateUnresolvedDepend(
|
||
DependOnName,
|
||
(PUNRESOLVED_DEPEND *) &DependOnRecord
|
||
);
|
||
|
||
if (status != NO_ERROR) {
|
||
goto ErrorExit;
|
||
}
|
||
|
||
//
|
||
// New unresolved depend entry created. We have to remove
|
||
// it if any error occurs later.
|
||
//
|
||
Type = TypeDependOnUnresolved;
|
||
|
||
}
|
||
else {
|
||
|
||
Type = TypeDependOnService;
|
||
}
|
||
|
||
if (status != NO_ERROR) {
|
||
goto ErrorExit;
|
||
}
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Depend on a group
|
||
//
|
||
|
||
PLOAD_ORDER_GROUP GroupEntry = ScGetOrderGroupList();
|
||
|
||
DependOnName++;
|
||
|
||
//
|
||
// Search for matching group name in load order list
|
||
//
|
||
while (GroupEntry != NULL) {
|
||
if (_wcsicmp(GroupEntry->GroupName, DependOnName) == 0) {
|
||
DependOnRecord = (PVOID) GroupEntry;
|
||
Type = TypeDependOnGroup;
|
||
break;
|
||
}
|
||
GroupEntry = GroupEntry->Next;
|
||
}
|
||
|
||
if (GroupEntry == NULL) {
|
||
//
|
||
// Could not find group in the OrderGroup list. Must
|
||
// depend on a standalone group.
|
||
//
|
||
status = ScCreateStandaloneGroup(
|
||
DependOnName,
|
||
(LPLOAD_ORDER_GROUP *) &DependOnRecord
|
||
);
|
||
|
||
if (status != NO_ERROR) {
|
||
goto ErrorExit;
|
||
}
|
||
|
||
Type = TypeDependOnGroup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Allocate memory for start depend record and insert it in the
|
||
// front of the start depend list of the service we are processing.
|
||
//
|
||
if ((status = ScCreateDependRecord(
|
||
TRUE, // For start list
|
||
ServiceRecord,
|
||
&Start
|
||
)) != NO_ERROR) {
|
||
|
||
goto ErrorExit;
|
||
}
|
||
|
||
//
|
||
// Start depend record created OK, set fields. Set stop
|
||
// depend if appropriate (Type == TypeDependOnService).
|
||
//
|
||
status = ScSetServiceDependList(
|
||
Start,
|
||
ServiceRecord,
|
||
DependOnRecord,
|
||
Type
|
||
);
|
||
|
||
if (status != NO_ERROR) {
|
||
|
||
//
|
||
// Remove the start depend record just created in the front of
|
||
// the start depend list and delete it.
|
||
//
|
||
ServiceRecord->StartDepend = Start->Next;
|
||
LocalFree(Start);
|
||
|
||
goto ErrorExit;
|
||
}
|
||
|
||
} // if got token
|
||
|
||
} // while there is a dependency
|
||
|
||
return NO_ERROR;
|
||
|
||
ErrorExit:
|
||
|
||
//
|
||
// Remove newly created service record because of errors and we cannot
|
||
// proceed.
|
||
//
|
||
if (Type == TypeDependOnUnresolved) {
|
||
|
||
ScDeleteUnresolvedDepend((PUNRESOLVED_DEPEND *) &DependOnRecord);
|
||
}
|
||
|
||
//
|
||
// Clean up dependencies created up to the point of failure
|
||
//
|
||
ScDeleteStartDependencies(ServiceRecord);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScCreateUnresolvedDepend(
|
||
IN LPWSTR Name,
|
||
OUT LPUNRESOLVED_DEPEND *Unresolved
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function looks for a matching unresolved entry in the unresolved
|
||
depend list. If a match is found, the reference count is incremented
|
||
and the pointer to the matching entry is returned.
|
||
|
||
If no matching entry is found, this function creates a new unresolved
|
||
entry, insert it into the end of the unresolved depend list, and
|
||
return a pointer to the new entry.
|
||
|
||
|
||
Arguments:
|
||
|
||
Name - Supplies the name of the service or group which has not been
|
||
installed yet, and thus needing this unresolved depend entry.
|
||
|
||
Unresolved - Receives a pointer to the unresolved entry.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR - The operation was successful.
|
||
|
||
ERROR_NOT_ENOUGH_MEMORY - Allocation of memory failed.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the caller has exclusively acquired the
|
||
database lock. It is called by ScCreateDependencies.
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
LPUNRESOLVED_DEPEND UnresolvedList = ScGetUnresolvedDependList();
|
||
|
||
BOOL Found = FALSE;
|
||
|
||
|
||
//
|
||
// Search the existing unresolved depend list for the matching
|
||
// unresolved depend entry.
|
||
//
|
||
while (UnresolvedList != NULL) {
|
||
|
||
if (_wcsicmp(UnresolvedList->Name, Name) == 0) {
|
||
Found = TRUE;
|
||
break;
|
||
}
|
||
|
||
UnresolvedList = UnresolvedList->Next;
|
||
}
|
||
|
||
if (Found) {
|
||
|
||
UnresolvedList->RefCount++;
|
||
|
||
SC_LOG2(DEPEND,
|
||
"Found existing unresolved entry for " FORMAT_LPWSTR
|
||
", just increment refcount to %lu\n", UnresolvedList->Name,
|
||
UnresolvedList->RefCount);
|
||
|
||
*Unresolved = UnresolvedList;
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
//
|
||
// Not found. Allocate a new unresolved entry.
|
||
//
|
||
if ((*Unresolved = (LPUNRESOLVED_DEPEND)LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
sizeof(UNRESOLVED_DEPEND) + WCSSIZE(Name)
|
||
)) == NULL) {
|
||
SC_LOG1(ERROR,"ScCreateUnresolvedDepend: LocalAlloc failure %lu\n",
|
||
GetLastError());
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
(*Unresolved)->Name = (LPWSTR) ((DWORD_PTR) *Unresolved +
|
||
sizeof(UNRESOLVED_DEPEND));
|
||
wcscpy((*Unresolved)->Name, Name);
|
||
|
||
(*Unresolved)->RefCount = 1;
|
||
|
||
SC_LOG1(DEPEND, "Created new unresolved depend entry "
|
||
FORMAT_LPWSTR "\n", (*Unresolved)->Name);
|
||
|
||
UnresolvedList = &UnresolvedDependList;
|
||
|
||
//
|
||
// Add the new unresolved entry to the unresolved list at the end.
|
||
//
|
||
ADD_TO_LIST(UnresolvedList, *Unresolved);
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
VOID
|
||
ScDeleteUnresolvedDepend(
|
||
IN OUT LPUNRESOLVED_DEPEND *Unresolved
|
||
)
|
||
{
|
||
|
||
if ((*Unresolved)->RefCount) {
|
||
(*Unresolved)->RefCount--;
|
||
SC_LOG1(DEPEND, "ScDeleteUnresolvedDepend: Subtracted RefCount is "
|
||
FORMAT_DWORD "\n", (*Unresolved)->RefCount);
|
||
}
|
||
else {
|
||
//
|
||
// The reference count better not be 0.
|
||
//
|
||
SC_LOG0(ERROR, "ScDeleteUnresolvedDepend: Before delete, refcount is 0!\n");
|
||
SC_ASSERT(FALSE);
|
||
}
|
||
|
||
if ((*Unresolved)->RefCount == 0) {
|
||
REMOVE_FROM_LIST(*Unresolved);
|
||
LocalFree(*Unresolved);
|
||
*Unresolved = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScCreateDependRecord(
|
||
IN BOOL IsStartList,
|
||
IN OUT PSERVICE_RECORD ServiceRecord,
|
||
OUT PDEPEND_RECORD *DependRecord
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates the memory for a depend record, and insert
|
||
it into the front of the specific depend list. If IsStartList is
|
||
TRUE, the depend record goes into the start depend list of
|
||
ServiceRecord, otherwise the depend record goes into the stop
|
||
depend list of the ServiceRecord.
|
||
|
||
Arguments:
|
||
|
||
IsStartList - TRUE indicates to insert into start list, FALSE indicates
|
||
to insert into stop list.
|
||
|
||
ServiceRecord - Receives the start/stop depend record in its dependency
|
||
list.
|
||
|
||
DependRecord - Receives a pointer to the new depend record created.
|
||
|
||
Return Value:
|
||
|
||
NO_ERROR - The operation was successful.
|
||
|
||
ERROR_NOT_ENOUGH_MEMORY - The call to allocate memory for a depend
|
||
record failed.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the caller has exclusively acquired the
|
||
database lock. It is called by ScCreateDependencies.
|
||
|
||
--*/
|
||
{
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
if ((*DependRecord = (PDEPEND_RECORD)LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
sizeof(DEPEND_RECORD)
|
||
)) == NULL) {
|
||
SC_LOG(ERROR,"ScCreateDependRecord: LocalAlloc failure %ld\n",
|
||
GetLastError());
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
|
||
//
|
||
// Insert the depend record into the front of the list
|
||
//
|
||
if (IsStartList) {
|
||
//
|
||
// Start depend
|
||
//
|
||
(*DependRecord)->Next = ServiceRecord->StartDepend;
|
||
ServiceRecord->StartDepend = *DependRecord;
|
||
}
|
||
else {
|
||
//
|
||
// Stop depend
|
||
//
|
||
(*DependRecord)->Next = ServiceRecord->StopDepend;
|
||
ServiceRecord->StopDepend = *DependRecord;
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
VOID
|
||
ScDeleteStartDependencies(
|
||
IN PSERVICE_RECORD ServiceRecord
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deletes the start dependencies list of a service. It also
|
||
deletes the the stop dependencies of other services which need this
|
||
service to be stopped first.
|
||
|
||
NOTE: This function is for call from RChangeServiceConfig.
|
||
|
||
Arguments:
|
||
|
||
ServiceRecord - Supplies the start dependencies information in this
|
||
service record.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the caller has exclusively acquired the
|
||
database lock. It is called by ScAddConfigInfoServiceRecord and
|
||
ScDecrementUseCountAndDelete.
|
||
|
||
It also assumes that the caller has exclusively acquired the group
|
||
list lock.
|
||
|
||
--*/
|
||
{
|
||
PDEPEND_RECORD StartEntry;
|
||
PDEPEND_RECORD StopEntry;
|
||
PDEPEND_RECORD StopBackPointer;
|
||
|
||
SC_ASSERT(ScGroupListLock.HaveExclusive());
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
StartEntry = ServiceRecord->StartDepend;
|
||
|
||
while (StartEntry != NULL) {
|
||
|
||
if (StartEntry->DependType == TypeDependOnService) {
|
||
|
||
LPSERVICE_RECORD DependencyService = StartEntry->DependService;
|
||
|
||
|
||
//
|
||
// Find the stop depend record for the service which depends on this
|
||
// service to be stopped first, and delete it.
|
||
//
|
||
StopEntry = DependencyService->StopDepend;
|
||
StopBackPointer = StopEntry;
|
||
|
||
while ((StopEntry != NULL) &&
|
||
(StopEntry->DependService != ServiceRecord)) {
|
||
|
||
StopBackPointer = StopEntry;
|
||
StopEntry = StopEntry->Next;
|
||
}
|
||
|
||
if (StopEntry == NULL) {
|
||
#ifndef _CAIRO_
|
||
//
|
||
// We allow Netlogon to appear in the start dependency list, but
|
||
// not in the stop dependency list. This is for the case where
|
||
// we add a "soft" dependency on netlogon because the service runs
|
||
// in a remove account.
|
||
//
|
||
if (_wcsicmp(DependencyService->ServiceName,L"Netlogon") != 0) {
|
||
#endif // _CAIRO_
|
||
SC_LOG1(ERROR,
|
||
"ScDeleteStartDependencies: Failed to find matching stop depend node for "
|
||
FORMAT_LPWSTR "\n",
|
||
DependencyService->ServiceName);
|
||
SC_ASSERT(FALSE);
|
||
return;
|
||
#ifndef _CAIRO_
|
||
}
|
||
#endif // _CAIRO_
|
||
}
|
||
else {
|
||
|
||
if (StopEntry->DependService == ServiceRecord) {
|
||
|
||
if ((PVOID) StopBackPointer == StopEntry) {
|
||
//
|
||
// Unchaining from the front of the list
|
||
//
|
||
DependencyService->StopDepend = StopEntry->Next;
|
||
}
|
||
else {
|
||
//
|
||
// Unchaining from the middle or end of the list
|
||
//
|
||
StopBackPointer->Next = StopEntry->Next;
|
||
}
|
||
|
||
LocalFree(StopEntry);
|
||
}
|
||
}
|
||
}
|
||
else if (StartEntry->DependType == TypeDependOnGroup) {
|
||
|
||
//
|
||
// Decrement the reference count on the standalone group
|
||
// entry and deletes it if 0.
|
||
//
|
||
if (StartEntry->DependGroup->RefCount != MAXULONG) {
|
||
ScDeleteStandaloneGroup(StartEntry->DependGroup);
|
||
}
|
||
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Dependency type is unresolved.
|
||
//
|
||
ScDeleteUnresolvedDepend(&StartEntry->DependUnresolved);
|
||
}
|
||
|
||
//
|
||
// Now delete the start depend record.
|
||
//
|
||
ServiceRecord->StartDepend = StartEntry->Next;
|
||
LocalFree(StartEntry);
|
||
StartEntry = ServiceRecord->StartDepend;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
ScDeleteStopDependencies(
|
||
IN PSERVICE_RECORD ServiceToBeDeleted
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deletes the stop dependencies list of a service. For
|
||
every stop depend service, it makes the start depend pointer of that
|
||
service to point to an unresolved depend entry.
|
||
|
||
This function is called when the service is to be deleted.
|
||
|
||
Arguments:
|
||
|
||
ServiceToBeDeleted - Supplies the pointer to the service that will
|
||
be deleted.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the caller has exclusively acquired the
|
||
database lock. It is called by ScDecrementUseCountAndDelete.
|
||
|
||
--*/
|
||
{
|
||
|
||
DWORD status;
|
||
PDEPEND_RECORD StartEntry;
|
||
PDEPEND_RECORD StopEntry;
|
||
LPUNRESOLVED_DEPEND Unresolved;
|
||
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
StopEntry = ServiceToBeDeleted->StopDepend;
|
||
|
||
while (StopEntry != NULL) {
|
||
|
||
LPSERVICE_RECORD DependencyService = StopEntry->DependService;
|
||
|
||
//
|
||
// Loop through the start depend entries of the service which
|
||
// depends on ServiceToBeDeleted.
|
||
//
|
||
StartEntry = DependencyService->StartDepend;
|
||
|
||
while (StartEntry != NULL) {
|
||
|
||
if (StartEntry->DependService == ServiceToBeDeleted) {
|
||
break;
|
||
}
|
||
|
||
StartEntry = StartEntry->Next;
|
||
}
|
||
|
||
if (StartEntry != NULL) {
|
||
|
||
//
|
||
// Found a start depend entry that points to the service to be.
|
||
// deleted. Make it point to an unresolved depend entry that
|
||
// represents that service.
|
||
//
|
||
status = ScCreateUnresolvedDepend(
|
||
ServiceToBeDeleted->ServiceName,
|
||
&Unresolved
|
||
);
|
||
|
||
if (status == NO_ERROR) {
|
||
StartEntry->DependType = TypeDependOnUnresolved;
|
||
StartEntry->DependUnresolved = Unresolved;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now delete the start depend record.
|
||
//
|
||
ServiceToBeDeleted->StopDepend = StopEntry->Next;
|
||
LocalFree(StopEntry);
|
||
StopEntry = ServiceToBeDeleted->StopDepend;
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScSetServiceDependList(
|
||
LPDEPEND_RECORD Start,
|
||
LPSERVICE_RECORD ServiceRecord,
|
||
PVOID DependOnRecord,
|
||
DEPEND_TYPE DependOnType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the caller has exclusively acquired the
|
||
database lock. It is called by ScResolveDependencyToService and
|
||
ScCreateDependencies.
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
LPDEPEND_RECORD Stop;
|
||
|
||
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
||
|
||
//
|
||
// Set fields for start depend entry.
|
||
//
|
||
Start->DependType = DependOnType;
|
||
Start->Depend = DependOnRecord;
|
||
|
||
if (DependOnType == TypeDependOnService) {
|
||
|
||
//
|
||
// Allocate memory for stop depend record and insert it in the
|
||
// front of the stop depend list of the service we depend on.
|
||
//
|
||
if ((status = ScCreateDependRecord(
|
||
FALSE, // For stop list
|
||
(LPSERVICE_RECORD) DependOnRecord,
|
||
&Stop
|
||
)) != NO_ERROR) {
|
||
|
||
return status;
|
||
}
|
||
|
||
Stop->DependType = TypeDependOnService;
|
||
Stop->DependService = ServiceRecord;
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
LPLOAD_ORDER_GROUP
|
||
ScGetNamedGroupRecord(
|
||
IN LPCWSTR GroupName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function searches for a named group, first in the order group
|
||
list and next in the standalone group list.
|
||
|
||
Arguments:
|
||
|
||
GroupName - Supplies the name of the group to look for.
|
||
|
||
Return Value:
|
||
|
||
Returns the pointer to group found. If not found, this value is
|
||
NULL.
|
||
|
||
Note:
|
||
|
||
This routine assumes that the caller has exclusively acquired the
|
||
group list lock.
|
||
|
||
--*/
|
||
{
|
||
LPLOAD_ORDER_GROUP Group;
|
||
|
||
for (Group = ScGetOrderGroupList();
|
||
Group != NULL;
|
||
Group = Group->Next)
|
||
{
|
||
if (_wcsicmp(Group->GroupName, GroupName) == 0)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (Group == NULL)
|
||
{
|
||
for (Group = ScGetStandaloneGroupList();
|
||
Group != NULL;
|
||
Group = Group->Next)
|
||
{
|
||
if (_wcsicmp(Group->GroupName, GroupName) == 0)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return Group;
|
||
}
|
||
|
||
|
||
VOID
|
||
ScGetUniqueTag(
|
||
IN LPWSTR GroupName,
|
||
OUT LPDWORD Tag
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function looks for a unique tag value within the specified
|
||
group.
|
||
|
||
Arguments:
|
||
|
||
GroupName - Specifies the group name within which the value tag
|
||
returned must be unique.
|
||
|
||
Tag - Receives the unique tag value.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Note:
|
||
|
||
This function acquires share access to the group list lock.
|
||
|
||
It assumes that the exclusive service database lock is already
|
||
acquired so that no other caller can execute this code until
|
||
the returned tag is fully assigned to the service, and that the
|
||
service entries in the database list don't change.
|
||
|
||
The GroupListLock must be held exclusively prior to calling this
|
||
function.
|
||
|
||
--*/
|
||
{
|
||
LPDWORD TagArray;
|
||
DWORD TagArrayLength;
|
||
|
||
DWORD ReturnTag = 1;
|
||
|
||
LPLOAD_ORDER_GROUP Group;
|
||
|
||
SC_ASSERT(ScGroupListLock.Have());
|
||
SC_ASSERT(ScServiceRecordLock.Have());
|
||
|
||
if (ScGetGroupVector(
|
||
GroupName,
|
||
(LPBYTE *) &TagArray,
|
||
&TagArrayLength
|
||
) == NO_ERROR) {
|
||
|
||
if (TagArray != NULL) {
|
||
//
|
||
// The returned values is actually the number of bytes. Divide it
|
||
// by size of DWORD to make it the number of array entries.
|
||
//
|
||
TagArrayLength = TagArrayLength / sizeof(DWORD);
|
||
|
||
SC_ASSERT((TagArrayLength - 1) == TagArray[0]);
|
||
|
||
ScCompareVector(
|
||
TagArray,
|
||
TagArrayLength,
|
||
&ReturnTag
|
||
);
|
||
}
|
||
}
|
||
else {
|
||
|
||
TagArray = NULL;
|
||
}
|
||
|
||
Group = ScGetNamedGroupRecord(GroupName);
|
||
|
||
if (Group != NULL) {
|
||
|
||
GroupAgain:
|
||
FOR_ALL_SERVICES(Service) {
|
||
|
||
if ((Service->RegistryGroup == Group) &&
|
||
(Service->Tag == ReturnTag)) {
|
||
|
||
ReturnTag++;
|
||
|
||
if (TagArray != NULL) {
|
||
|
||
ScCompareVector(
|
||
TagArray,
|
||
TagArrayLength,
|
||
&ReturnTag
|
||
);
|
||
}
|
||
|
||
goto GroupAgain;
|
||
}
|
||
} // while all services
|
||
}
|
||
|
||
|
||
*Tag = ReturnTag;
|
||
|
||
SC_LOG(DEPEND, "ScGetUniqueTag: Tag=" FORMAT_DWORD "\n", *Tag);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
ScCompareVector(
|
||
IN LPDWORD TagArray,
|
||
IN DWORD TagArrayLength,
|
||
IN OUT LPDWORD ReturnTagPtr
|
||
)
|
||
{
|
||
DWORD i;
|
||
|
||
VectorAgain:
|
||
for (i = 1; i < TagArrayLength; i++) {
|
||
|
||
if (TagArray[i] == (*ReturnTagPtr)) {
|
||
|
||
SC_LOG(DEPEND_DUMP, "Tag " FORMAT_DWORD " is not unique\n",
|
||
*ReturnTagPtr);
|
||
|
||
(*ReturnTagPtr)++;
|
||
goto VectorAgain;
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
ScGetDependencySize(
|
||
LPSERVICE_RECORD ServiceRecord,
|
||
LPDWORD DependSize,
|
||
LPDWORD MaxDependSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
ServiceRecord -
|
||
|
||
DependSize - This points to a location that will contain the number
|
||
of bytes required for the list of dependency strings.
|
||
|
||
MaxDependSize - This points to a location that will contain the
|
||
number of bytes in the longest dependency string in the set.
|
||
|
||
Return Value:
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
LPDEPEND_RECORD dependRecord;
|
||
DWORD bytesNeeded = 0;
|
||
DWORD StrSize=0;
|
||
|
||
dependRecord = ServiceRecord->StartDepend;
|
||
|
||
//
|
||
// NOTE: Dependencies are expected to be a double NULL terminated
|
||
// terminated set of strings.
|
||
//
|
||
bytesNeeded += sizeof(WCHAR);
|
||
|
||
if (dependRecord == NULL) {
|
||
bytesNeeded += sizeof(WCHAR);
|
||
}
|
||
while (dependRecord != NULL) {
|
||
|
||
SC_ASSERT( dependRecord->Depend != NULL );
|
||
|
||
// Add room. WCSSIZE adds 1 char. For final entry, we'll
|
||
// use null char. In between, we'll put some separator.
|
||
|
||
if (dependRecord->DependType == TypeDependOnService) {
|
||
StrSize =
|
||
(DWORD) WCSSIZE(dependRecord->DependService->ServiceName); // sizes...
|
||
|
||
}
|
||
else if (dependRecord->DependType == TypeDependOnGroup) {
|
||
StrSize =
|
||
(DWORD) WCSSIZE(dependRecord->DependGroup->GroupName) +
|
||
sizeof(WCHAR); // name size plus SC_GROUP_IDENTIFIERW
|
||
}
|
||
else {
|
||
//
|
||
// Unresolved service dependency
|
||
//
|
||
StrSize = (DWORD) WCSSIZE(dependRecord->DependUnresolved->Name);
|
||
}
|
||
|
||
bytesNeeded += StrSize;
|
||
|
||
if (StrSize > *MaxDependSize) {
|
||
*MaxDependSize = StrSize;
|
||
}
|
||
|
||
dependRecord = dependRecord->Next;
|
||
}
|
||
*DependSize = bytesNeeded;
|
||
}
|
||
|
||
DWORD
|
||
ScGetDependencyString(
|
||
LPSERVICE_RECORD ServiceRecord,
|
||
DWORD MaxDependSize,
|
||
DWORD DependSize,
|
||
LPWSTR lpDependencies
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
ServiceRecord -
|
||
|
||
MaxDependSize - This is the size of the largest string in the
|
||
dependency list.
|
||
|
||
lpDependencies - This is a pointer to the location where the
|
||
list of dependency strings is to be stored.
|
||
|
||
Return Value:
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
LPWSTR endOfVariableData;
|
||
LPWSTR fixedDataEnd;
|
||
LPDEPEND_RECORD dependRecord;
|
||
DWORD bufSize;
|
||
DWORD ApiStatus = NO_ERROR;
|
||
|
||
//
|
||
// Put dependencies in the return buffer. Since it is a NULL-NULL
|
||
// string, put an extra NULL at the end to begin with.
|
||
//
|
||
endOfVariableData = (LPWSTR) (((LPBYTE)lpDependencies) + DependSize);
|
||
|
||
endOfVariableData = endOfVariableData - 1;
|
||
* endOfVariableData = L'\0';
|
||
|
||
fixedDataEnd = lpDependencies;
|
||
|
||
dependRecord = ServiceRecord->StartDepend;
|
||
|
||
if (dependRecord == NULL) {
|
||
//
|
||
// If there are no dependencies, then we need to add a separator
|
||
// that will be followed by the NULL (immediately above).
|
||
// This separator is used to get us across the RPC interface.
|
||
// Then on the client side, it is changed to a NULL. So we end
|
||
// up with an empty-double-NULL-terminated-string.
|
||
//
|
||
endOfVariableData = endOfVariableData - 1;
|
||
* endOfVariableData = L'/';
|
||
lpDependencies = endOfVariableData;
|
||
}
|
||
else {
|
||
|
||
LPWSTR DependName;
|
||
|
||
|
||
DependName = (LPWSTR)LocalAlloc(0, (UINT) MaxDependSize);
|
||
|
||
if (DependName == NULL) {
|
||
ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto Cleanup;
|
||
}
|
||
|
||
lpDependencies = endOfVariableData;
|
||
while (dependRecord != NULL) {
|
||
|
||
SC_ASSERT( dependRecord->Depend != NULL );
|
||
|
||
|
||
if (dependRecord->DependType == TypeDependOnService) {
|
||
|
||
wcscpy(DependName, dependRecord->DependService->ServiceName);
|
||
|
||
}
|
||
else if (dependRecord->DependType == TypeDependOnGroup) {
|
||
|
||
*DependName = SC_GROUP_IDENTIFIERW;
|
||
|
||
wcscpy(DependName + 1, dependRecord->DependGroup->GroupName);
|
||
}
|
||
else {
|
||
//
|
||
// Unresolved service dependency
|
||
//
|
||
wcscpy(DependName, dependRecord->DependUnresolved->Name);
|
||
}
|
||
|
||
bufSize = (DWORD) wcslen(DependName);
|
||
|
||
if ( !ScCopyStringToBufferW (
|
||
DependName,
|
||
bufSize,
|
||
fixedDataEnd,
|
||
&endOfVariableData,
|
||
&lpDependencies,
|
||
NULL
|
||
) ) {
|
||
|
||
SC_LOG0(ERROR,
|
||
"RQueryServiceConfigW:ScCopyStringtoBufferW (Dependencies)Failed\n");
|
||
|
||
SC_ASSERT( FALSE );
|
||
ApiStatus = ERROR_INSUFFICIENT_BUFFER;
|
||
LocalFree(DependName);
|
||
goto Cleanup;
|
||
}
|
||
else {
|
||
//
|
||
// Add separator character.
|
||
//
|
||
|
||
lpDependencies[bufSize] = L'/';
|
||
}
|
||
|
||
dependRecord = dependRecord->Next;
|
||
}
|
||
|
||
LocalFree(DependName);
|
||
}
|
||
Cleanup:
|
||
return(ApiStatus);
|
||
}
|
||
|
||
|
||
#if DBG
|
||
VOID
|
||
ScDumpGroups(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function walks group list prints out each entry.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Note:
|
||
|
||
Calls to this routine must be enclosed within #if DBG.
|
||
|
||
--*/
|
||
{
|
||
PLOAD_ORDER_GROUP GroupEntry = ScGetOrderGroupList();
|
||
|
||
|
||
while (GroupEntry != NULL) {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
"\nOrdered Groups:\n"));
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
"Group: Handle=%08lx Name=%ws RefCount=x%lx\n",
|
||
GroupEntry,
|
||
GroupEntry->GroupName,
|
||
GroupEntry->RefCount));
|
||
|
||
GroupEntry = GroupEntry->Next;
|
||
}
|
||
|
||
GroupEntry = ScGetStandaloneGroupList();
|
||
|
||
while (GroupEntry != NULL) {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
"Standalone Groups:\n"));
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
"Group: Handle=%08lx Name=%ws RefCount=x%lx\n",
|
||
GroupEntry,
|
||
GroupEntry->GroupName,
|
||
GroupEntry->RefCount));
|
||
|
||
GroupEntry = GroupEntry->Next;
|
||
}
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
"\nTDI group is at %08lx" "\nPNP_TDI group is at %08lx\n",
|
||
ScGlobalTDIGroup,
|
||
ScGlobalPNP_TDIGroup));
|
||
}
|
||
|
||
VOID
|
||
ScDumpServiceDependencies(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function walks the start and stop dependencies lists of every
|
||
service in the service record list.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Note:
|
||
|
||
Calls to this routine must be enclosed within #if DBG.
|
||
|
||
--*/
|
||
{
|
||
PDEPEND_RECORD DependList;
|
||
|
||
FOR_ALL_SERVICES(ServiceRecord)
|
||
{
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
"Service: %-20ws UseCount=%lu",
|
||
ServiceRecord->ServiceName,
|
||
ServiceRecord->UseCount));
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
" MemberOfGroup=%08lx ",
|
||
ServiceRecord->MemberOfGroup));
|
||
|
||
if (ServiceRecord->MemberOfGroup != NULL) {
|
||
if (ServiceRecord->MemberOfGroup->RefCount != MAXULONG) {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
"SG=%ws\n",
|
||
ServiceRecord->MemberOfGroup->GroupName));
|
||
}
|
||
else if (ServiceRecord->MemberOfGroup->RefCount == MAXULONG) {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
"OG=%ws\n",
|
||
ServiceRecord->MemberOfGroup->GroupName));
|
||
}
|
||
}
|
||
else {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID, DEBUG_DEPEND_DUMP, "\n"));
|
||
}
|
||
|
||
if (ServiceRecord->RegistryGroup != NULL) {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
" RG=%ws\n",
|
||
ServiceRecord->RegistryGroup->GroupName));
|
||
}
|
||
|
||
//
|
||
// Dump start depend
|
||
//
|
||
DependList = ServiceRecord->StartDepend;
|
||
|
||
if (DependList != NULL) {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID, DEBUG_DEPEND_DUMP, " StartDepend:\n"));
|
||
}
|
||
|
||
while (DependList != NULL) {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
" %ws\n",
|
||
DependList->DependService->ServiceName));
|
||
|
||
DependList = DependList->Next;
|
||
}
|
||
|
||
//
|
||
// Dump stop depend
|
||
//
|
||
DependList = ServiceRecord->StopDepend;
|
||
if (DependList != NULL) {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID, DEBUG_DEPEND_DUMP, " StopDepend:\n"));
|
||
}
|
||
|
||
while (DependList != NULL) {
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_DEPEND_DUMP,
|
||
" %ws\n",
|
||
DependList->DependService->ServiceName));
|
||
|
||
DependList = DependList->Next;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif // #if DBG
|