windows-nt/Source/XPSP1/NT/termsrv/remdsk/rds/t120/mst120/domain.cpp
2020-09-26 16:20:57 +08:00

6002 lines
173 KiB
C++
Raw Permalink 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.

#include "precomp.h"
DEBUG_FILEZONE(ZONE_T120_MCSNC);
/*
* domain.cpp
*
* Copyright (c) 1993 - 1995 by DataBeam Corporation, Lexington, KY
*
* Abstract:
* This is the implementation file for the domain class. The domain
* class encapsulates a single instance of a domain information base.
* This class include all code necessary to modify and act upon that
* information base. Almost all activity into and out of objects of
* this class is in the form of MCS commands. These commands are
* implemented as virtual member functions that are inherited from its
* base class CommandTarget. These commands are essentially the same
* as the Protocol Data Units (PDUs) defined in T.125.
*
* This class inherits from CommandTarget, which is where the virtual
* member functions for each command is defined. All commands that are
* handled by this class are overridden by it. Life for a domain object
* consists of receiving these commands and responding by transmitting
* these commands. A Domain object has no need for its own "thread" of
* execution.
*
* Instances of the domain class maintain an information base that is
* used to determine how to respond to these commands. The commands are
* not only routed according to the state of the information base, but also
* act to change the information base. When two MCS providers are
* connected, the domain objects within them become logically linked. This
* means that they exchange these commands in such a manner as to guarantee
* the MCS services for which the providers are responsible.
*
* When a domain object is first instantiated, its information base is
* empty. That is, it has no user attachments, no MCS connections, no
* channels, no tokens, and no queued requests. As the domain object
* processes commands, the information base takes shape, and all subsequent
* activity follows that shape.
*
* By necessity, there is a lot of complex code in this module. This is to
* allow for all the timing problems that can occur in a distributed
* network, such as MCS provides for. In order to reduce the complexity
* as much as possible, this class does NOT worry about certains things,
* as follows:
*
* The Domain class does NOT include code to perform any kind of flow
* control. When a send data command comes in to a domain, it is sent out
* to any attachment that is to receive it. It is assumed that any
* buffering and flow control is handled by the attachments.
*
* For the most part the domain class does NOT distinguish between user
* attachments and MCS connections. To the domain, they are merely
* referred to as "attachments". MCS connections can be either upward or
* downward attachments. User attachments can only be downward
* attachments. In the case where a user detaches and the domain needs to
* know if the whole attachment is gone or just one user, it can check an
* entry in its attachment dictionary to determine the type. Most of the
* time it does not care. Most confirms and indications are routed to user
* attachments in exactly the same way they are routed to MCS connections.
*
* Domain objects do not worry about memory management. They merely pass
* packet objects from place to place. They NEVER look at the contents
* of the packet objects. It is assumed that the attachments have
* allocated memory for the user data that is being passed around.
*
* Where possible, behavior that is specific to channels and tokens has
* been relegated to those classes. It is necessary for the domain to
* handle channel and token behavior for IDs that do not exist.
*
* Private Instance Variables:
* Merge_State
* This is current merge state that the domain is in. These states
* are detailed in "domain.h".
* Outstanding_Merge_Requests
* This is a counter showing the number of outstanding merge requests.
* The domain object uses this to know when an in-process merge is
* complete.
* Number_Of_Users
* This is the number of users in the domain.
* Number_Of_Channels
* This is the number of channels in the domain.
* Number_Of_Tokens
* This is the number of tokens in the domain.
* Domain_Parameters
* This is a structure that contains the currently negotiated domain
* parameters. These parameters are used to validate requests, such
* as the adding of a new user.
* Domain_Parameters_Locked
* This is a boolean flag that indicates whether or not the domain
* parameters have been locked into place yet. This locking will
* occur when the domain object accepts its first MCS connection.
* m_pConnToTopProvider
* This is a pointer to the attachment that represents the link to the
* top provider. Note that this provider may be several hops away
* from the top provider, so this really just points in the direction
* of the top provider. If this pointer is NULL, this THIS is the
* top provider.
* m_AttachmentList
* This is a list of the downward attachments that this domain is
* aware of. Remeber that this list can contain any combination of
* user attachments and MCS connections. They are treated equally
* for most things.
* m_AttachUserQueue
* This is a list of outstanding attach user requests. It is necessary
* to remember these requests so that they can answered in the same
* order in which they arrived.
* m_MergeQueue
* During a merge operation, this queue is used to remember how to
* route merge confirms back to their originators. The assumption is
* made that an upward provider will always respond to merge requests
* in the same order that they were received in (a valid assumption
* for our implementation). Also note that this implementation
* currently only merges one resource type at a time, so only one queue
* is necessary. For example, user IDs are merged, then static
* channels, and so on.
* m_ChannelList2
* This is a list of channel objects that correspond to active channels
* within this domain. When a channel object exists, the domain lets
* it handle all channel related activity (such as approving who can
* join a channel).
* m_TokenList2
* This is a list of token objects that correspond to active tokens
* within this domain. When a token object exists, the domain lets it
* handle all token related activity (such as approving who can inhibit
* the token).
* m_nDomainHeight
* This instance variable contains the height of the domain from the
* point-of-view of this provider. If there are two layers of
* providers below this one, then the height will be two.
* m_DomainHeightList2
* This is a list of domain heights that were registered from all
* downward attachments. This allows the current provider to
* automatically update domain height when a downward attachment is
* lost.
* Random_Channel_Generator
* This object is used by this domain to generate random channel IDs.
*
* Private Member Functions:
* LockDomainParameters
* This member function is used to change the values of the locally
* maintained domain parameters structure. Passing NULL to it causes
* it to set a default set of parameters. The second parameter allows
* the caller to specify whether or not these new parameters are
* "locked" into the domain (meaning that they cannot change since they
* have been locked in by acceptance of the first connection).
* AllocateDynamicChannel
* This routine randomly selects a channel ID from the dynamic range.
* ValidateUserID
* This routine checks to see if the specified user is in the sub-tree
* of this domain. It can optionally check to see if the user is at
* a specific attachment in the sub-tree.
* PurgeDomain
* This routine purges the entire domain. This means terminating all
* attachments, and freeing up all resources. This results in
* returning the domain to its initialized state.
* DeleteAttachment
* This routine deletes a specified attachment and frees up all
* resources associated with that attachment.
* DeleteUser
* This routine deletes a user from the domain. This takes care of
* deleting the attachment too if this were a locally attach user.
* DeleteChannel
* This routine deletes a specific channel from the information base.
* DeleteToken
* This routine deletes a specific token from the information base.
* ReclaimResources
* This routine iterates through both the channel list and the token
* list, asking each if is still valid (and removing those that are
* not). This allows for automatic "garbage collection" when users
* or attachments are lost.
* MergeInformationBase
* This routine issues the appropriate merge requests to a pending
* top provider during a domain merger operation. It is also a state
* machine in that it remembers what has already been merged, so that
* the next time it is called, it can merge the next set of resources.
* SetMergeState
* This routine sets the merge state of the object, and if necessary,
* issues a MergeDomainIndication to all downward attachments.
* AddChannel
* This routine is used to add a new channel to the current channel
* list during a merge operation.
* AddToken
* This routine is used to add a new token to the current token list
* during a merge operation.
* CalculateDomainHeight
* This routine calculates the height of the current domain, and takes
* appropriate action if the height limit has been exceeded.
*
* Caveats:
* None.
*
* Author:
* James P. Galvin, Jr.
*/
#include "plgxprt.h"
/*
* External Interfaces
*/
/*
* These macros are used when requesting a random dynamic channel ID.
*/
#define DYNAMIC_CHANNEL_LOW_EXTENT 1001
#define DYNAMIC_CHANNEL_HIGH_EXTENT 65535L
/*
* These two static structure are used by all instances of the domain class
* as the minimum and maximum supported values for the domain parameters.
*/
static DomainParameters Static_Minimum_Domain_Parameters =
{
MINIMUM_MAXIMUM_CHANNELS,
MINIMUM_MAXIMUM_USERS,
MINIMUM_MAXIMUM_TOKENS,
MINIMUM_NUMBER_OF_PRIORITIES,
MINIMUM_MINIMUM_THROUGHPUT,
MINIMUM_MAXIMUM_DOMAIN_HEIGHT,
MINIMUM_MAXIMUM_PDU_SIZE,
MINIMUM_PROTOCOL_VERSION
};
static DomainParameters Static_Maximum_Domain_Parameters =
{
(UShort) MAXIMUM_MAXIMUM_CHANNELS,
(UShort) MAXIMUM_MAXIMUM_USERS,
(UShort) MAXIMUM_MAXIMUM_TOKENS,
MAXIMUM_NUMBER_OF_PRIORITIES,
MAXIMUM_MINIMUM_THROUGHPUT,
MAXIMUM_MAXIMUM_DOMAIN_HEIGHT,
MAXIMUM_MAXIMUM_PDU_SIZE,
MAXIMUM_PROTOCOL_VERSION
};
/*
* This is now set to 0 to indicate that this provider does not perform
* any type of throughput enforcement.
*/
#define DEFAULT_THROUGHPUT_ENFORCEMENT_INTERVAL 0
/*
* These macros define the number of buckets to used for each of the hash
* dictionaries maintained by this class.
*/
#define CHANNEL_LIST_NUMBER_OF_BUCKETS 16
/*
* Domain ()
*
* Public
*
* Functional Description:
* This is the constructor for the domain class. It merely initailizes
* all instance variables to indicate an "empty" state. It also sets
* the initial state of the domain parameters array.
*/
Domain::Domain()
:
m_AttachmentList(),
m_ChannelList2(CHANNEL_LIST_NUMBER_OF_BUCKETS),
m_TokenList2(),
m_DomainHeightList2(),
m_pConnToTopProvider(NULL),
Merge_State(MERGE_INACTIVE),
Outstanding_Merge_Requests(0),
Number_Of_Users(0),
Number_Of_Channels(0),
Number_Of_Tokens(0),
m_nDomainHeight(0)
{
/*
* Set the domain parameters to their default values.
*/
LockDomainParameters (NULL, FALSE);
}
/*
* ~Domain ()
*
* Public
*
* Functional Description:
* This is the destructor for the domain class. All it does is purge the
* entire domain, which means to return it to its initial state (all
* attachments are broken).
*/
Domain::~Domain ()
{
PurgeDomain (REASON_USER_REQUESTED);
}
/*
* BOOL IsTopProvider ()
*
* Public
*
* Functional Description:
* This routine returns TRUE if this is the top provider, and FALSE
* otherwise.
*/
/*
* Void GetDomainParameters ()
*
* Public
*
* Functional Description:
* This routine returns the currently active minimum and maximum domain
* parameter values (which will be different depending on whether or not
* the domain parameters have been locked yet).
*/
Void Domain::GetDomainParameters (
PDomainParameters domain_parameters,
PDomainParameters min_domain_parameters,
PDomainParameters max_domain_parameters)
{
/*
* Load the currently in-use set of domain parameters.
*/
if (domain_parameters != NULL)
*domain_parameters = Domain_Parameters;
/*
* See if domain parameters are already locked in for this domain.
*/
if (Domain_Parameters_Locked)
{
/*
* The domain parameters for this domain have already been locked
* during the creation of a previous connection. Return those values
* as both the minimum and maximum values (no deviation will be
* permitted).
*/
if (min_domain_parameters != NULL)
*min_domain_parameters = Domain_Parameters;
if (max_domain_parameters != NULL)
*max_domain_parameters = Domain_Parameters;
}
else
{
/*
* Domain parameters have not yet been locked. Therefore, return the
* minimum and maximum values imposed by this implementation.
*/
if (min_domain_parameters != NULL)
*min_domain_parameters = Static_Minimum_Domain_Parameters;
if (max_domain_parameters != NULL)
*max_domain_parameters = Static_Maximum_Domain_Parameters;
}
}
/*
* Void BindConnAttmnt ()
*
* Public
*
* Functional Description:
* This routine allows an attachment to bind to the domain. It takes all
* actions appropriate to the addition of a new attachment (upward or
* downward).
*/
Void Domain::BindConnAttmnt (
PConnection pOrigConn,
BOOL upward_connection,
PDomainParameters domain_parameters)
{
CAttachment *pAtt;
PUser pUser;
PChannel channel;
PToken token;
/*
* Check the hierarchical direction of the requested attachment.
*/
if (upward_connection)
{
/*
* This is to be an upward connection. We must now check to make
* sure that we don't already have an upward connection.
*/
if (NULL == m_pConnToTopProvider)
{
/*
* This attachment is the new Top Provider.
*/
TRACE_OUT(("Domain::BindConnAttmnt: accepting upward attachment"));
m_pConnToTopProvider = pOrigConn;
/*
* Tell all channel objects who the new Top Provider is.
*/
m_ChannelList2.Reset();
while (NULL != (channel = m_ChannelList2.Iterate()))
{
channel->SetTopProvider(m_pConnToTopProvider);
}
/*
* Tell all token objects who the new Top Provider is.
*/
m_TokenList2.Reset();
while (NULL != (token = m_TokenList2.Iterate()))
{
token->SetTopProvider(m_pConnToTopProvider);
}
/*
* If the domain parameters have not yet been locked, then lock
* these into place.
*/
if (Domain_Parameters_Locked == FALSE)
{
TRACE_OUT(("Domain::BindConnAttmnt: locking domain parameters"));
LockDomainParameters (domain_parameters, TRUE);
/*
* Send a SetDomainParameters to each downward attachment.
* This will allow those objects to adjust their construction
* of send data PDUs to conform to the arbitrated maximum PDU
* size.
*/
m_AttachmentList.Reset();
while (NULL != (pUser = m_AttachmentList.IterateUser()))
{
pUser->SetDomainParameters(&Domain_Parameters);
}
}
/*
* Since we have bound to a provider above us, it is necessary to
* inform that provider of our height in the domain (otherwise
* the new Top Provider would have no way of knowing what the
* total height of the domain is). This is done by issuing an
* erect domain request upward.
*/
m_pConnToTopProvider->ErectDomainRequest(m_nDomainHeight, DEFAULT_THROUGHPUT_ENFORCEMENT_INTERVAL);
/*
* Now that this provider has become the former top provider of
* a lower domain, it is necessary to issue a plumb domain
* indication to all downward attachments. The primary reason
* for this is to assure that there are no cycles in the domain.
*/
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->PlumbDomainIndication(Domain_Parameters.max_height);
}
/*
* We now have a new top provider, which means that we must begin
* an information base merger.
*/
MergeInformationBase ();
}
else
{
/*
* We already have an upward connection (or one pending).
* Therefore, this attachment must be rejected.
*/
ERROR_OUT(("Domain::BindConnAttmnt: domain not hierarchical"));
pOrigConn->DisconnectProviderUltimatum(REASON_PROVIDER_INITIATED);
}
}
else
{
/*
* This is to be a downward connection. We must now check to see if
* we already have a record of the specified connection.
*/
if (! m_AttachmentList.FindConn(pOrigConn))
{
/*
* This does represent a new downward connection. So put it into
* the attachment list.
*/
TRACE_OUT(("Domain::BindConnAttmnt: accepting downward attachment"));
m_AttachmentList.AppendConn(pOrigConn);
/*
* If the domain parameters have not yet been locked, then lock
* these into place.
*/
if (Domain_Parameters_Locked == FALSE)
{
TRACE_OUT(("Domain::BindConnAttmnt: locking domain parameters"));
LockDomainParameters (domain_parameters, TRUE);
/*
* Send a SetDomainParameters to each downward attachment.
* This will allow those objects to adjust their construction
* of send data PDUs to conform to the arbitrated maximum PDU
* size.
*/
m_AttachmentList.Reset();
while (NULL != (pUser = m_AttachmentList.IterateUser()))
{
pUser->SetDomainParameters(&Domain_Parameters);
}
}
}
else
{
/*
* The attachment is already listed in the attachment list, so
* print an error and ignore the request.
*/
ERROR_OUT(("Domain::BindConnAttmnt: attachment already exists"));
}
}
}
/*
* Void PlumbDomainIndication ()
*
* Public
*
* Functional Description:
* This member function originates at a hgher provider and travels downward
* in the domain. Each provider examines the height limit, and disconnects
* if it is zero. If not, then the indication is forwarded downward.
*/
Void Domain::PlumbDomainIndication (
PConnection pOrigConn,
ULong height_limit)
{
/*
* Make sure that this indication is from the top provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* Check the height limit to determine whether this provider is too
* far from the top or not.
*/
if (height_limit != 0)
{
CAttachment *pAtt;
/*
* We are okay, so decrement the height limit and forward the
* indication to all downward attachments.
*/
TRACE_OUT(("Domain::PlumbDomainIndication: forwarding indication downward"));
height_limit--;
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->PlumbDomainIndication(height_limit);
}
}
else
{
/*
* We are too far from the top (which may indicate the existence
* of a cycle in the domain). It is therefore necessary to
* purge the entire domain (from this provider down).
*/
WARNING_OUT(("Domain::PlumbDomainIndication: purging domain"));
PurgeDomain (REASON_PROVIDER_INITIATED);
}
}
else
{
/*
* This indication was received from an attachment that is unknown to
* this domain. Ignore it.
*/
ERROR_OUT(("Domain::PlumbDomainIndication: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void ErectDomainRequest ()
*
* Public
*
* Functional Description:
* This member function is called whenever a lower provider detects a
* change in their domain height. This will be due to someone below this
* provider creating or breaking a connection.
*/
Void Domain::ErectDomainRequest (
PConnection pOrigConn,
ULong height_in_domain,
ULong)
{
/*
* Make sure that this request comes from an attachment that the local
* provider is aware of.
*/
if (m_AttachmentList.FindConn(pOrigConn))
{
/*
* Put the domain height into the domain height list, and then call
* the subroutine responsible for determining whether any action is
* required as a result of change in the height.
*/
TRACE_OUT(("Domain::ErectDomainRequest: processing request"));
m_DomainHeightList2.Append(pOrigConn, height_in_domain);
CalculateDomainHeight ();
}
else
{
/*
* The attachment is unknown to this provider. Ignore the request.
*/
ERROR_OUT(("Domain::ErectDomainRequest: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void MergeChannelsRequest ()
*
* Public
*
* Functional Description:
* This public member function is called by a former top provider during
* a domain merge operation. It travels upward to the top provider of
* the combined domain, where the merge can be processed. Any providers
* that it travels through on the way must remember how to route the
* confirm back to the originator.
*/
Void Domain::MergeChannelsRequest (
PConnection pOrigConn,
CChannelAttributesList *merge_channel_list,
CChannelIDList *purge_channel_list)
{
PChannelAttributes merge_channel;
Channel_Type channel_type;
ChannelID channel_id;
PChannel channel;
CChannelAttributesList merge_confirm_list;
/*
* Make sure that this request is coming from a legitimate downward
* attachment before processing it.
*/
if (m_AttachmentList.FindConn(pOrigConn))
{
/*
* Is this the top provider. If so the request can be processed
* locally. If not, it must be forwarded towards the top provider.
*/
if (IsTopProvider())
{
/*
* Iterate through the merge channel list, admitting all channels
* that can be admitted.
*/
merge_channel_list->Reset();
while (NULL != (merge_channel = merge_channel_list->Iterate()))
{
/*
* Get the address of the next channel attributes structure
* in the list. Then get the type and the ID of the channel
* being merged.
*/
channel_type = merge_channel->channel_type;
switch (channel_type)
{
case STATIC_CHANNEL:
channel_id = merge_channel->
u.static_channel_attributes.channel_id;
break;
case USER_CHANNEL:
channel_id = merge_channel->
u.user_channel_attributes.user_id;
break;
case PRIVATE_CHANNEL:
channel_id = merge_channel->
u.private_channel_attributes.channel_id;
break;
case ASSIGNED_CHANNEL:
channel_id = merge_channel->
u.assigned_channel_attributes.channel_id;
break;
}
/*
* Check to see if the channel being merged exists in the
* upper domain information base.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
if ((channel_type == STATIC_CHANNEL) &&
(channel->GetChannelType () == STATIC_CHANNEL))
{
/*
* It is a static channel. This means that the merge
* is okay (merging static channels is no problem).
*/
TRACE_OUT(("Domain::MergeChannelsRequest: static channel merge successful"));
/*
* Static channels are automatically joined.
* Note that sending an initiator ID of 0 tells the
* channel object not to issue a ChannelJoinConfirm,
* which is inappropriate during a merge.
*/
channel->ChannelJoinRequest(pOrigConn, 0, 0);
/*
* Put the channel attributes structure into the
* merge confirm list, meaning that the information
* associated with the successful merge will be
* repeated in the subsequent confirm.
*/
merge_confirm_list.Append(merge_channel);
}
else
{
/*
* The channel being merged is an in-use dynamic
* channel. Therefore, it must be rejected (this is
* NOT permitted).
*/
WARNING_OUT(("Domain::MergeChannelsRequest: dynamic channel in use - rejecting merge"));
/*
* Add the channel ID to the list of those channels
* to be purged frmo the lower domain.
*/
purge_channel_list->Append(channel_id);
}
}
else
{
/*
* If the channel does not exist in the upper domain at
* all, then add it to the upper domain.
*/
AddChannel(pOrigConn, merge_channel, &merge_confirm_list, purge_channel_list);
}
}
/*
* Send the appropriate merge channels confirm to the originating
* user.
*/
pOrigConn->MergeChannelsConfirm(&merge_confirm_list, purge_channel_list);
}
else
{
/*
* If this is not the top provider, then add the requesting
* attachment to the merge queue (which is used to route
* confirms back later), and forward the request upward towards
* the top provier.
*/
TRACE_OUT(("Domain::MergeChannelsRequest: forwarding request to Top Provider"));
m_MergeQueue.Append(pOrigConn);
m_pConnToTopProvider->MergeChannelsRequest(merge_channel_list, purge_channel_list);
}
}
else
{
/*
* This request was received from an attachment that is unknown to
* this domain.
*/
ERROR_OUT(("Domain::MergeChannelsRequest: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void MergeChannelsConfirm ()
*
* Public
*
* Functional Description:
* This public member function is called in response to a previous channels
* merge request. It is forwarded back down the hierarchy until it reaches
* the former top provider that initiated the request. That former top
* provider will use the information contained therein to determine
* whether the merge on a particular channel was successful or not. If
* it was not, then the channel is purged from the lower domain, and a
* purge channels indication is sent downward to let everyone in the lower
* domain know of this.
*/
Void Domain::MergeChannelsConfirm (
PConnection pOrigConn,
CChannelAttributesList *merge_channel_list,
CChannelIDList *purge_channel_list)
{
PConnection pConn;
PChannelAttributes merge_channel;
Channel_Type channel_type;
ChannelID channel_id;
PChannel channel;
BOOL joined;
CChannelAttributesList merge_confirm_list;
CUidList purge_user_list;
CChannelIDList purge_normal_list;
/*
* Verify that the confirm came from the top provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* Now check the merge state. If the state is inactive, then that
* means that this provider is an intermediate provider (i.e. a
* provider that lies between the top provider and the former top
* provider of the lower domain). If the state is not inactive, then
* this must be the former top provider of the lower domain.
*/
if (Merge_State == MERGE_INACTIVE)
{
/*
* This is a legitimate merge channels confirm. We must forward
* the confirm to the downward attachment that originated the
* merge channel request. We remember who this is by pulling
* out the first entry in the merge queue. Check to make sure
* that there is an entry in the merge queue.
*/
if (NULL != (pConn = m_MergeQueue.Get()))
{
/*
* Get the attachment that is to receive the confirm and verify
* that it is still connected (the connection could have been
* lost since the request was forwarded upward).
*/
if (m_AttachmentList.FindConn(pConn))
{
/*
* Iterate through the merge channel list, adding each of
* the channels it contains into the local information
* base.
*/
merge_channel_list->Reset();
while (NULL != (merge_channel = merge_channel_list->Iterate()))
{
/*
* Get the next channel to be merge and then get its
* channel ID.
*/
channel_type = merge_channel->channel_type;
switch (channel_type)
{
case STATIC_CHANNEL:
channel_id = merge_channel->
u.static_channel_attributes.channel_id;
joined = TRUE;
break;
case USER_CHANNEL:
channel_id = merge_channel->
u.user_channel_attributes.user_id;
joined = merge_channel->
u.user_channel_attributes.joined;
break;
case PRIVATE_CHANNEL:
channel_id = merge_channel->
u.private_channel_attributes.channel_id;
joined = merge_channel->
u.private_channel_attributes.joined;
break;
case ASSIGNED_CHANNEL:
channel_id = merge_channel->
u.assigned_channel_attributes.channel_id;
joined = TRUE;
break;
}
/*
* See if the channel already exists in the local
* information base.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
/*
* If the attachment is joined to this channel,
* then join it at this level too. Note that
* sending an initiator ID of 0 tells the channel
* object not to issue a ChannelJoinConfirm, which
* would be inappropriate during a merge.
*/
TRACE_OUT(("Domain::MergeChannelsConfirm: attempting to join merged channel"));
if (joined)
channel->ChannelJoinRequest(pConn, 0, 0);
/*
* Add the channel to the merge confirm list so
* that it will automatically be forwarded
* downward.
*/
merge_confirm_list.Append(merge_channel);
}
else
{
/*
* The channel does not exist in the local
* information base, so add it.
*/
AddChannel(pConn, merge_channel, &merge_confirm_list, purge_channel_list);
}
}
/*
* Forward the merge channel confirm on to the attachment
* from which the request originated.
*/
pConn->MergeChannelsConfirm(&merge_confirm_list, purge_channel_list);
}
else
{
/*
* The attachment from which the merge request originated
* has been lost. It may be necessary to send something
* to the Top Provider in order to guarantee the integrity
* of the domain. In some cases it may be necessary to
* purge the domain.
*/
WARNING_OUT(("Domain::MergeChannelsConfirm: forwarding attachment lost"));
}
}
else
{
/*
* There is no outstanding merge request that can be used to
* direct the confirm. This will happen only if a confirm
* is received without a previous merge having been sent.
* The proper response should be to send a RejectUltimatum
* to the offending upward attachment.
*/
ERROR_OUT(("Domain::MergeChannelsConfirm: merge queue empty"));
}
}
else
{
/*
* This confirm should not be received unless there is at least
* one outstanding merge request. Check to make sure that this
* is so.
*/
if (Outstanding_Merge_Requests != 0)
{
/*
* If there are any entries in the purge channel list, then
* it is necessary to issue a purge channels indication to all
* downward attachments.
*/
if (purge_channel_list->IsEmpty() == FALSE)
{
ChannelID chid;
UserID uid;
/*
* Iterate through the list of channels to be purged,
* putting each channel into either the "user list" or the
* "normal list". This separation is necessary for lower
* providers to be able to issue the appropriate
* indications.
*/
purge_channel_list->Reset();
while (NULL != (channel_id = purge_channel_list->Iterate()))
{
/*
* Get the channel ID of the next channel to be purged.
*/
TRACE_OUT(("Domain::MergeChannelsConfirm: merge rejected on channel ID = %04X", (UINT) channel_id));
/*
* Make sure the channel still exists locally before
* trying to purge it.
*/
if (m_ChannelList2.Find(channel_id))
{
/*
* Determine what type of channel is being purged
* and add it to the appropriate list. These lists
* will be used when issuing the purge channels
* indication below.
*/
if (ValidateUserID (channel_id, NULL))
purge_user_list.Append(channel_id);
else
purge_normal_list.Append(channel_id);
}
else
{
/*
* The channel to be purged could not be found in
* the local domain.
*/
ERROR_OUT(("Domain::MergeChannelsConfirm: no such channel"));
}
}
/*
* This loop simply transmits a PurgeChannelsIndication to
* all downward attachments in the lower domain.
*/
CAttachment *pAtt;
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->PurgeChannelsIndication(&purge_user_list, &purge_normal_list);
}
/*
* Iterate through the list of channels to be purged,
* deleting each channel.
*/
purge_normal_list.Reset();
while (NULL != (chid = purge_normal_list.Iterate()))
{
DeleteChannel(chid);
}
/*
* Iterate through the list of users to be purged, deleting
* each user.
*/
purge_user_list.Reset();
while (NULL != (uid = purge_user_list.Iterate()))
{
DeleteUser(uid);
}
}
/*
* Decrement the number of outstanding requests. If this
* was the last outstanding request, then go back to the
* merge state machine to see if there is anything left to
* do.
*/
if (--Outstanding_Merge_Requests == 0)
MergeInformationBase ();
}
else
{
/*
* There are no merge requests pending, so this errant confirm
* must be ignored.
*/
ERROR_OUT(("Domain::MergeChannelsConfirm: no outstanding merge requests"));
}
}
}
else
{
/*
* This confirm was received from someone besides the top provider.
*/
ERROR_OUT(("Domain::MergeChannelsConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void PurgeChannelsIndication ()
*
* Public
*
* Functional Description:
* This public member function is called in response to channels being
* purged from the lower domain during an information base merge operation.
* The purge is forwarded downward to all attachments. Then the channel
* are deleted from the local information base. For each user channel
* all resources in use by that user will be reclaimed.
*/
Void Domain::PurgeChannelsIndication (
PConnection pOrigConn,
CUidList *purge_user_list,
CChannelIDList *purge_channel_list)
{
CAttachment *pAtt;
UserID uid;
ChannelID chid;
/*
* Make sure this indication came from the top provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* This loop re-transmits the purge channel indication to all
* downward attachments.
*/
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->PurgeChannelsIndication(purge_user_list, purge_channel_list);
}
/*
* Iterate through the list of channels to be purged, deleting each
* channel.
*/
purge_channel_list->Reset();
while (NULL != (chid = purge_channel_list->Iterate()))
{
/*
* See if the specified channel is in the local information base.
* If it is not, ignore it (this is a normal condition during a
* purge operation).
*/
if (m_ChannelList2.Find(chid))
{
/*
* Check to see if the channel ID corresponds to a user ID
* channel. If it does, report the error and do nothing. If
* it is not a user ID channel, then delete the channel.
*/
if (ValidateUserID(chid, NULL) == FALSE)
{
/*
* Delete the channel.
*/
DeleteChannel(chid);
}
else
{
/*
* The specified channel is in the Channel List, but it
* does not refer to a user channel. This indicates that
* an error has occurred at the upward provider. Ignore
* the indication.
*/
ERROR_OUT(("Domain::PurgeChannelsIndication: UserChannel in purge_channel_list"));
}
}
}
/*
* Iterate through the list of users to be purged, deleting
* each one.
*/
purge_user_list->Reset();
while (NULL != (uid = purge_user_list->Iterate()))
{
/*
* See if the specified user is in the local information base.
* If it is not, ignore it (this is a normal condition during a
* purge operation).
*/
if (m_ChannelList2.Find(uid))
{
/*
* Check to see if the user ID corresponds to a valid user in
* the sub-tree of this provider.
*/
if (ValidateUserID(uid, NULL))
{
/*
* Delete the user from the local information base.
*/
DeleteUser(uid);
}
else
{
/*
* The specified ID is in the Channel List, but it does not
* refer to a user channel. This indicates that an error
* has occurred at the upward provider. Ignore the
* indication.
*/
ERROR_OUT(("Domain::PurgeChannelsIndication: non-UserChannel in purge_user_list"));
}
}
}
}
else
{
/*
* This indication was received from someone besides the top provider.
*/
ERROR_OUT(("Domain::PurgeChannelsIndication: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void MergeTokensRequest ()
*
* Public
*
* Functional Description:
* This public member function is called by a former top provider during
* a domain merge operation. It travels upward to the top provider of
* the combined domain, where the merge can be processed. Any providers
* that it travels through on the way must remember how to route the
* confirm back to the originator.
*/
Void Domain::MergeTokensRequest (
PConnection pOrigConn,
CTokenAttributesList *merge_token_list,
CTokenIDList *purge_token_list)
{
PTokenAttributes merge_token;
TokenState token_state;
TokenID token_id;
PToken token;
CUidList *owner_list;
UserID uid;
CTokenAttributesList merge_confirm_list;
/*
* Make sure that this request is coming from a legitimate downward
* attachment before processing it.
*/
if (m_AttachmentList.FindConn(pOrigConn))
{
/*
* Is this the top provider. If so the request can be processed
* locally. If not, it must be forwarded toward the top provider.
*/
if (IsTopProvider())
{
/*
* Iterate through the merge token list, attempting to add each
* token in sequence.
*/
merge_token_list->Reset();
while (NULL != (merge_token = merge_token_list->Iterate()))
{
/*
* Get the address of the structure containing the next token
* to merge. Then get the token ID from the structure.
*/
token_state = merge_token->token_state;
switch (token_state)
{
case TOKEN_GRABBED:
token_id = merge_token->
u.grabbed_token_attributes.token_id;
break;
case TOKEN_INHIBITED:
token_id = merge_token->
u.inhibited_token_attributes.token_id;
break;
case TOKEN_GIVING:
token_id = merge_token->
u.giving_token_attributes.token_id;
break;
case TOKEN_GIVEN:
token_id = merge_token->
u.given_token_attributes.token_id;
break;
}
/*
* Check to see if the requested token is in the local
* information base.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
/*
* If the token already exists within this domain, then
* we need to compare the state of the local token and
* the state of the token being merged. If they are
* both inhibited, then the merge operation can proceed
* successfully. However, if either one is something
* besides inhibited, then the merge request will be
* rejected.
*/
if ((token_state == TOKEN_INHIBITED) &&
(token->GetTokenState () == TOKEN_INHIBITED))
{
/*
* Add each inhibiting user from the former lower
* domain to the token for this domain.
*/
TRACE_OUT(("Domain::MergeTokensRequest: merging inhibiting user IDs"));
owner_list = merge_token->
u.inhibited_token_attributes.inhibitors;
owner_list->Reset();
while (NULL != (uid = owner_list->Iterate()))
{
token->TokenInhibitRequest (NULL, uid, token_id);
}
/*
* Add the token attributes structure to the merge
* list, so that it will be included as part of the
* merge tokens confirm.
*/
merge_confirm_list.Append(merge_token);
}
else
{
/*
* The token is in use in the upper domain, and a merge
* is not possible. So add the token ID to the purge
* list, so that it will be purged from the lower
* domain.
*/
WARNING_OUT(("Domain::MergeTokensRequest: token in use - rejecting merge"));
purge_token_list->Append(token_id);
}
}
else
{
/*
* The token does not exist in the local information base.
* Attempt to add it.
*/
AddToken (merge_token, &merge_confirm_list,
purge_token_list);
}
}
/*
* Issue the merge tokens confirm to the originator of the request.
*/
pOrigConn->MergeTokensConfirm(&merge_confirm_list, purge_token_list);
}
else
{
/*
* This must be an intermediate provider in the upper domain.
* Forward the request upward to be handled by the Top Provider
* of the upper domain. Also append the identity of the
* requestor to the merge queue, so that the pending response
* can be routed appropriately.
*/
TRACE_OUT(("Domain::MergeTokensRequest: forwarding request to Top Provider"));
m_MergeQueue.Append(pOrigConn);
m_pConnToTopProvider->MergeTokensRequest(merge_token_list, purge_token_list);
}
}
else
{
/*
* This request is coming from a provider that is unknown in this
* domain. Simply ignore the request.
*/
ERROR_OUT(("Domain::MergeTokensRequest: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void MergeTokensConfirm ()
*
* Public
*
* Functional Description:
* This MCS command is initially invoked by the Top Provider of the upper
* domain during a domain merge operation. It travels downward until it
* reaches the former Top Provider of the lower domain. It contains
* notification of whether or not the merge of the token was successful.
*/
Void Domain::MergeTokensConfirm (
PConnection pOrigConn,
CTokenAttributesList *merge_token_list,
CTokenIDList *purge_token_list)
{
PConnection pConn;
PTokenAttributes merge_token;
TokenState token_state;
TokenID token_id;
PToken token;
CUidList *owner_list;
UserID uid;
CTokenAttributesList merge_confirm_list;
/*
* Check to make sure that it came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* Now check the merge state. If the state is inactive, then that
* means that this provider is an intermediate provider (i.e. a
* provider that lies between the top provider and the former top
* provider of the lower domain). If the state is not inactive, then
* this must be the former top provider of the lower domain.
*/
if (Merge_State == MERGE_INACTIVE)
{
/*
* Since this came from the Top Provider, it should be a response
* to an outstanding merge request that passed through this
* provider. If so, then the merge queue will not be empty. Check
* this before proceeding with the request.
*/
if (NULL != (pConn = m_MergeQueue.Get()))
{
/*
* Get the identity of the provider to which this confirm must
* forwarded.
*/
/*
* If the provider is still attached to this provider, then
* forward the merge confirm.
*/
if (m_AttachmentList.FindConn(pConn))
{
/*
* Iterate through the merge token list, attempting to add
* each token in sequence.
*/
merge_token_list->Reset();
while (NULL != (merge_token = merge_token_list->Iterate()))
{
/*
* Get the address of the structure containing the next
* token to merge. Then get the token ID from the
* structure.
*/
token_state = merge_token->token_state;
switch (token_state)
{
case TOKEN_GRABBED:
token_id = merge_token->
u.grabbed_token_attributes.token_id;
break;
case TOKEN_INHIBITED:
token_id = merge_token->
u.inhibited_token_attributes.token_id;
break;
case TOKEN_GIVING:
token_id = merge_token->
u.giving_token_attributes.token_id;
break;
case TOKEN_GIVEN:
token_id = merge_token->
u.given_token_attributes.token_id;
break;
}
/*
* Check to see if the requested token is in the local
* information base.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
/*
* The token already exists in the information base
* of this intermediate provider. The only valid
* case where this could happen is if the token
* being merged is inhibited in both upper and
* lower domains. Check this.
*/
if ((token_state == TOKEN_INHIBITED) &&
(token->GetTokenState () == TOKEN_INHIBITED))
{
/*
* Add each inhibiting user from the former
* lower domain to the token for this domain.
*/
TRACE_OUT(("Domain::MergeTokensConfirm: merging inhibiting user IDs"));
owner_list = merge_token->
u.inhibited_token_attributes.inhibitors;
owner_list->Reset();
while (NULL != (uid = owner_list->Iterate()))
{
token->TokenInhibitRequest(NULL, uid, token_id);
}
/*
* Add the token attributes structure to the
* merge list, so that it will be included as
* part of the merge tokens confirm.
*/
merge_confirm_list.Append(merge_token);
}
else
{
/*
* The states of the tokens in the upper and
* lower domain are invalid. This should have
* been resolved by the Top Provider before
* issuing this merge request. Report the
* error and continue.
*/
ERROR_OUT(("Domain::MergeTokensConfirm: bad token in merge confirm"));
}
}
else
{
/*
* The token does not exist in the local
* information base. Attempt to add it.
*/
AddToken (merge_token, &merge_confirm_list,
purge_token_list);
}
}
/*
* Forward merge confirm toward the former top provider
* of the lower domain.
*/
pConn->MergeTokensConfirm(&merge_confirm_list, purge_token_list);
}
else
{
/*
* The provider from which the outstanding request came
* must have been lost since the request was initially
* forwarded upward. We need to issue some notification
* of this upward, depending on the response within the
* confirm.
*/
ERROR_OUT(("Domain::MergeTokensConfirm: forwarding attachment lost"));
}
}
else
{
/*
* There is no outstanding request with which this confirm is
* associated. Something is wrong above. All this provider
* can do is ignore the errant confirm.
*/
ERROR_OUT (("Domain::MergeTokensConfirm: no outstanding merge requests"));
}
}
else
{
/*
* If we have received a confirm from the top provider, hen there
* should be at least one outstanding merge request. Make sure
* this is true before proceeding.
*/
if (Outstanding_Merge_Requests != 0)
{
/*
* If there are any entries in the purge token list, it is
* necessary to issue a purge tokens indication to all
* downward attachments.
*/
if (purge_token_list->IsEmpty() == FALSE)
{
/*
* Issue a PurgeTokensIndication downward to all
* attachments.
*/
CAttachment *pAtt;
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->PurgeTokensIndication(this, purge_token_list);
}
/*
* Iterate through the list of tokens to be purged,
* removing each from the local information base.
*/
purge_token_list->Reset();
while (NULL != (token_id = purge_token_list->Iterate()))
{
DeleteToken (token_id);
}
}
/*
* Decrement the number of outstanding merge requests. If
* there are now no more, then proceed to the next state
* in the merger state machine.
*/
if (--Outstanding_Merge_Requests == 0)
MergeInformationBase ();
}
else
{
/*
* We have received a merge confirm when there are no
* outstanding merge requests. Ignore the confirm.
*/
ERROR_OUT(("Domain::MergeTokensConfirm: no outstanding merge requests"));
}
}
}
else
{
/*
* This merge confirm has been received from someone besides the top
* provider. Ignore it.
*/
ERROR_OUT(("Domain::MergeTokensConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void PurgeTokensIndication ()
*
* Public
*
* Functional Description:
* This member function is first invoked by the former Top Provider of
* the lower domain during a merge operation. This indicates that a
* token merge into the upper domain was rejected. After verifying that
* this MCS command is valid, is should simply be repeated downward to all
* attachments.
*/
Void Domain::PurgeTokensIndication (
PConnection pOrigConn,
CTokenIDList *purge_token_list)
{
CAttachment *pAtt;
TokenID token_id;
/*
* Check to make sure that this MCS command came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* This is a valid command. Iterate through the attachment list,
* forwarding the command to everyone below this provider in the
* domain hierarchy.
*/
TRACE_OUT(("Domain::PurgeTokensIndication: forwarding indication to all attachments"));
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->PurgeTokensIndication(this, purge_token_list);
}
/*
* Iterate through the list of tokens to be purged, deleting each one.
*/
purge_token_list->Reset();
while (NULL != (token_id = purge_token_list->Iterate()))
{
/*
* See if the specified token is in the local information base.
* If it is not ignore it (this is a normal condition during a
* purge operation). If it is, then delete it.
*/
if (m_TokenList2.Find(token_id))
DeleteToken (token_id);
}
}
else
{
/*
* This indication was received from someone besides the Top Provider.
* Ignore it.
*/
ERROR_OUT(("Domain::PurgeTokensIndication: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void DisconnectProviderUltimatum ()
*
* Public
*
* Functional Description:
* This MCS command is generated whenever an attachment becomes invalid.
* The local provider must respond by breaking all ties to the attachment.
* If the attachment is to the Top Provider, this will cause the domain
* to completely eradicate itself (return to the initialized state).
*
* Note that when an attachment is lost, it is not removed from the
* merge queue. This allows this provider to continue to route
* outstanding merge confirms appropriately, even when one of the
* attachments is lost. Removing the attachments from the merge queue
* here will result in outstanding merge confirms being directed to the
* wrong attachments.
*/
Void Domain::DisconnectProviderUltimatum (
CAttachment *pOrigAtt,
Reason reason)
{
/*
* If we lost the connection to the Top Provider, we have no choice but
* to purge the entire domain. Ways of preventing this drastic action
* are being studied, but for now this implementation conforms to the
* definition of T.125.
*/
if (pOrigAtt == m_pConnToTopProvider)
{
ASSERT(pOrigAtt->IsConnAttachment());
TRACE_OUT(("Domain::DisconnectProviderUltimatum: purging entire domain"));
m_pConnToTopProvider = NULL;
PurgeDomain (reason);
}
/*
* If we lose a downward attachment, then we must free up all resources
* associated with that attachment. This is handled by a private member
* function.
*/
if (m_AttachmentList.Find(pOrigAtt))
{
TRACE_OUT(("Domain::DisconnectProviderUltimatum: deleting downward attachment=0x%p", pOrigAtt));
DeleteAttachment(pOrigAtt, reason);
}
/*
* If we lost an attachment that has an outstanding AttachUserRequest,
* go ahead and remove it from the attach user queue. Note that this
* works differently from the merge queue. With AttachUserConfirms, it
* makes no difference what order they are processed in, so we can do
* this. With Merge???Confirms, they MUST be processed in order, so
* we leave the lost attachment in the queue, and allow the confirm
* command handler to deal with the fact that the attachment is no
* longer valid.
*/
while (m_AttachUserQueue.Remove(pOrigAtt))
{
TRACE_OUT(("Domain::DisconnectProviderUltimatum: pending user attachment deleted=0x%p", pOrigAtt));
}
}
/*
* Void RejectUltimatum ()
*
* Public
*
* Functional Description:
* This member function is called when a provider detects a PDU that it
* cannot correctly process, the default behavior is to disconnect the
* connection that conveys the PDU.
*/
Void Domain::RejectUltimatum (
PConnection pOrigConn,
Diagnostic,
PUChar,
ULong)
{
/*
* Send a disconnect provider ultimatum to the attachment that has accused
* us of wrongdoing.
*/
pOrigConn->DisconnectProviderUltimatum(REASON_PROVIDER_INITIATED);
/*
* Simulate the reception of a disconnect provider ultimatum from that
* same attachment. This will cause the connection to be cleanly broken
* on both sides.
*/
DisconnectProviderUltimatum(pOrigConn, REASON_PROVIDER_INITIATED);
}
/*
* Void AttachUserRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initiated by a user attachment, when a new user
* wishes to attach to this domain. It is forwarded upward to the Top
* Provider of the domain, who ultimately has to process the request.
*/
Void Domain::AttachUserRequest (
CAttachment *pOrigAtt)
{
UserID user_id;
PChannel channel;
/*
* Check to see if this is the Top Provider or not. If it is, then the
* request can be processed locally. If not, then the request must be
* forwarded upward to the Top Provider.
*/
if (IsTopProvider())
{
/*
* This is the Top Provider, so process the request here. Check to
* see if the arbitrated domain parameters allow the addition of
* a new user to the domain.
*/
if (Number_Of_Users < Domain_Parameters.max_user_ids)
{
/*
* Also check to see if the arbitrated domain parameters allow the
* addition of a new channel to the domain (since a user is also
* channel).
*/
if (Number_Of_Channels < Domain_Parameters.max_channel_ids)
{
/*
* Adding a new user is not a problem. Get a unique ID to use
* as the user ID, and then create a new UserChannel object.
*/
user_id = AllocateDynamicChannel ();
DBG_SAVE_FILE_LINE
channel = new UserChannel(user_id, pOrigAtt, this, m_pConnToTopProvider,
&m_ChannelList2, &m_AttachmentList);
if (channel != NULL)
{
/*
* Add the new channel object to the channel list. Note
* that it is not necessary for this object to issue the
* attach user confirm, because that was handled by the
* constructor of the UserChannel object.
*/
TRACE_OUT(("Domain::AttachUserRequest: adding user ID = %04X", (UINT) user_id));
m_ChannelList2.Insert(user_id, channel);
Number_Of_Users++;
Number_Of_Channels++;
/*
* If this represents an attachment that did not previously
* exist, then this must be a local user attachment. Add
* it to the attachment list as such.
*/
if (! m_AttachmentList.Find(pOrigAtt))
{
ASSERT(pOrigAtt->IsUserAttachment());
m_AttachmentList.Append(pOrigAtt);
}
}
else
{
/*
* The allocation of the UserChannel object failed. Issue
* an unsuccessful attach user confirm.
*/
ERROR_OUT(("Domain::AttachUserRequest: user allocation failed"));
pOrigAtt->AttachUserConfirm(RESULT_UNSPECIFIED_FAILURE, 0);
}
}
else
{
/*
* The negotiated domain parameters will not allow a new
* channel to be added to the domain. Reject the request.
*/
ERROR_OUT(("Domain::AttachUserRequest: too many channels"));
pOrigAtt->AttachUserConfirm(RESULT_TOO_MANY_CHANNELS, 0);
}
}
else
{
/*
* The negotiated domain parameters will not allow a new user
* to be added to the domain. Reject the request.
*/
ERROR_OUT(("Domain::AttachUserRequest: too many users"));
pOrigAtt->AttachUserConfirm(RESULT_TOO_MANY_USERS, 0);
}
}
else
{
/*
* This is not the Top Provider, so the request must be forwarded
* upward toward the Top Provider. Add the originator of the request
* to the attach user queue, so that this provider can properly route
* the returning confirm (when it arrives).
*/
TRACE_OUT(("Domain::AttachUserRequest: adding attachment to attach user queue"));
m_AttachUserQueue.Append(pOrigAtt);
m_pConnToTopProvider->AttachUserRequest();
}
}
/*
* Void AttachUserConfirm ()
*
* Public
*
* Functional Description:
* This MCS command is initially generated by the Top Provider upon
* receipt of an AttachUserRequest. It contains the result of that
* request. If the result is successful, then it also contains the user
* ID for the new user. This confirm needs to be routed all the was back
* to the user attachment that originated the request.
*/
Void Domain::AttachUserConfirm (
PConnection pOrigConn,
Result result,
UserID uidInitiator)
{
CAttachment *pAtt;
PChannel channel;
CUidList detach_user_list;
/*
* Make sure that the request originated with the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* The reception of this confirm means that there should be an
* outstanding request. Make sure this is the case before proceeding.*
*/
if (NULL != (pAtt = m_AttachUserQueue.Get()))
{
/*
* There is an outstanding request. Get the identity of the
* attachment from which the request originated.
*/
/*
* If the result was successful, then it is necessary for this
* provider to create a UserChannel object in the local information
* base for the new user.
*/
if (result == RESULT_SUCCESSFUL)
{
/*
* Make sure the channel ID is not already in use before
* proceeding.
*/
if (! m_ChannelList2.Find(uidInitiator))
{
/*
* Create a new UserChannel object, using the ID generated
* by the Top Provider.
*/
DBG_SAVE_FILE_LINE
channel = new UserChannel(uidInitiator, pAtt, this, m_pConnToTopProvider,
&m_ChannelList2, &m_AttachmentList);
if (channel != NULL)
{
/*
* Add the UserChannel object to the channel list.
*/
TRACE_OUT(("Domain::AttachUserConfirm: adding user ID = %04X", (UINT) uidInitiator));
m_ChannelList2.Insert(uidInitiator, channel);
Number_Of_Users++;
Number_Of_Channels++;
/*
* If the user's attachment is not already in the
* attachment list, then this must be a new local
* attachment. Add it to the attachment list as such.
*/
if (! m_AttachmentList.Find(pAtt))
{
ASSERT(pAtt->IsUserAttachment());
m_AttachmentList.Append(pAtt);
}
}
else
{
/*
* The local provider was unable to allocate the
* UserChannel object. This means that the new user
* must be removed from the domain. To do this, send
* a DetachUserRequest to the Top Provider and an
* unsuccessful AttachUserConfirm to the originator
* of the request.
*/
ERROR_OUT(("Domain::AttachUserConfirm: user allocation failed"));
detach_user_list.Append(uidInitiator);
m_pConnToTopProvider->DetachUserRequest(REASON_PROVIDER_INITIATED, &detach_user_list);
pAtt->AttachUserConfirm(RESULT_UNSPECIFIED_FAILURE, 0);
}
}
else
{
/*
* The ID associated with this confirm is already in use.
* This indicates that something is wrong above. This
* provider has no choice but to ignore the confirm.
*/
WARNING_OUT(("Domain::AttachUserConfirm: channel ID already in use"));
}
}
else
{
/*
* Since the result of the attach was not successful, this
* provider does not have to add anything to its channel list.
* The only required action is to forward the confirm to the
* originating user.
*/
TRACE_OUT(("Domain::AttachUserConfirm: echoing failed confirm"));
pAtt->AttachUserConfirm(result, uidInitiator);
}
}
else
{
/*
* The attach user queue is empty. This probably indicates that
* the connection to the user who originated the request was lost
* before the confirm got back. This provider doesn't need to
* do anything except issue a DetachUserRequest (if the confirm
* indicates that the attach operation was successful).
*/
WARNING_OUT(("Domain::AttachUserConfirm: attach user queue empty"));
if (result == RESULT_SUCCESSFUL)
{
TRACE_OUT (("Domain::AttachUserConfirm: sending DetachUserRequest"));
detach_user_list.Append(uidInitiator);
m_pConnToTopProvider->DetachUserRequest(REASON_DOMAIN_DISCONNECTED, &detach_user_list);
}
}
}
else
{
/*
* This confirm was received from someone besides the Top Provider.
* Ignore it.
*/
ERROR_OUT(("Domain::AttachUserConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void DetachUserRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initiated by a user attachment that wishes to leave
* the domain. After validation, delete the user from the information base
* pass the request upward.
*/
Void Domain::DetachUserRequest (
CAttachment *pOrigAtt,
Reason reason,
CUidList *user_id_list)
{
UserID uid;
CUidList detach_user_list;
/*
* Iterate through the list of users named to be deleted.
*/
user_id_list->Reset();
while (NULL != (uid = user_id_list->Iterate()))
{
/*
* Make sure the user really exists in the sub-tree from which this
* request originated.
*/
if (ValidateUserID(uid, pOrigAtt))
{
/*
* Delete the user from the local information base.
*/
DeleteUser(uid);
/*
* Put the user ID into the list of validated user IDs.
*/
detach_user_list.Append(uid);
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated.
*/
WARNING_OUT(("Domain::DetachUserRequest: invalid user ID"));
}
}
/*
* Check to see if there are any users to be deleted. If so, then process
* the request.
*/
if (detach_user_list.IsEmpty() == FALSE)
{
/*
* Check to see if this is the Top Provider.
*/
if (IsTopProvider())
{
/*
* This is the Top Provider, so issue a detach user indication to
* all downward attachments.
*/
TRACE_OUT(("Domain::DetachUserRequest: sending DetachUserIndication to all attachments"));
CAttachment *pAtt;
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->DetachUserIndication(reason, &detach_user_list);
}
}
else
{
/*
* This is not the Top Provider, so forward the detach user
* request upward.
*/
TRACE_OUT(("Domain::DetachUserRequest: forwarding DetachUserRequest to Top Provider"));
m_pConnToTopProvider->DetachUserRequest(reason, &detach_user_list);
}
}
else
{
/*
* The user ID list contained no valid entries, so ignore the request.
*/
ERROR_OUT(("Domain::DetachUserRequest: no valid user IDs"));
}
}
/*
* Void DetachUserIndication ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the Top Provider in response to
* a user detaching from the domain (willingly or otherwise). It is
* forwarded downward in the hierarchy where it will eventually reach all
* providers and their user attachments.
*/
Void Domain::DetachUserIndication (
PConnection pOrigConn,
Reason reason,
CUidList *user_id_list)
{
UserID uid;
/*
* Verify that the indication came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* The first thing to do is repeat the indication to all downward
* attachments. Keep in mind that this sends the detach indication
* to user attachments as well as remote connections.
*/
TRACE_OUT(("Domain::DetachUserIndication: forwarding DetachUserIndication to all attachments"));
CAttachment *pAtt;
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->DetachUserIndication(reason, user_id_list);
}
/*
* Iterate through the list of users, deleting those that are in
* the sub-tree of this provider.
*/
user_id_list->Reset();
while (NULL != (uid = user_id_list->Iterate()))
{
/*
* Check to see if this user is somewhere in the sub-tree of this
* provider. If so it is necessary to delete the user channel from
* the channel list. Note that it is perfectly normal to receive a
* detach user indication for a user that is not in the sub-tree of
* the receiving provider.
*/
if (ValidateUserID(uid, NULL) )
{
/*
* Delete the user from the local information base.
*/
DeleteUser(uid);
}
}
}
else
{
/*
* This indication was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::DetachUserIndication: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void ChannelJoinRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by a user attachment that wishes
* to join a channel. It flows upward in the hierarchy until it reaches
* a provider who is already joined to the channel. That provider (which
* is not necessarily the Top Provider), will issue a channel join
* confirm, indicating whether or not the join was successful.
*/
Void Domain::ChannelJoinRequest (
CAttachment *pOrigAtt,
UserID uidInitiator,
ChannelID channel_id)
{
PChannel channel;
ChannelID requested_id;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* See if the channel already exists in the local information base.
* If so, then let the Channel object handle the join request.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
TRACE_OUT(("Domain::ChannelJoinRequest: sending join request to channel object"));
channel->ChannelJoinRequest(pOrigAtt, uidInitiator, channel_id);
}
else
{
/*
* The channel does not already exist in the channel list. Check
* to see if this is the Top Provider. If so, we can try to
* add the channel to the list. If this is not the Top Provider,
* then we simply forward the request upward.
*/
if (IsTopProvider())
{
/*
* Save the value of the channel the user originally attempted
* to join. This may change if this is a request to join
* channel 0 (an assigned channel).
*/
requested_id = channel_id;
/*
* We already know the channel does not exist in the channel
* list. Therefore, this is a valid request only if the
* channel being joined is a static channel or channel 0 (which
* is interpreted as a request for an assigned channel).
* Dynamic channels (those above 1000) can only be joined if
* they already exist.
*/
if (requested_id <= 1000)
{
/*
* See if the arbitrated domain parameters will allow the
* addition of a new channel.
*/
if (Number_Of_Channels < Domain_Parameters.max_channel_ids)
{
/*
* If this is a request for an assigned channel, then
* allocate a random channel ID in the dynamic range.
* Then create a new Channel object.
*/
if (requested_id == 0)
channel_id = AllocateDynamicChannel ();
DBG_SAVE_FILE_LINE
channel = new Channel(channel_id, this, m_pConnToTopProvider,
&m_ChannelList2, &m_AttachmentList);
if (channel != NULL)
{
/*
* The creation of the new channel was successful.
* Add it to the channel list.
*/
TRACE_OUT(("Domain::ChannelJoinRequest: adding channel ID = %04X", (UINT) channel_id));
m_ChannelList2.Insert(channel_id, channel);
Number_Of_Channels++;
/*
* When new channels are created, they are
* initially empty. So we must join the
* originating attachment to the newly created
* attachment. This will also cause a channel
* join confirm to be issued to the originator.
*/
channel->ChannelJoinRequest(pOrigAtt, uidInitiator, requested_id);
}
else
{
/*
* Allocation of the Channel object failed. We
* must therefore issue an unsuccessful channel
* join confirm to the originating attachment.
*/
ERROR_OUT(("Domain::ChannelJoinRequest: channel allocation failed"));
pOrigAtt->ChannelJoinConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, requested_id, 0);
}
}
else
{
/*
* Domain parmeters will not allow the addition of
* any more channels. Fail the request.
*/
ERROR_OUT(("Domain::ChannelJoinRequest: join denied - too many channels"));
pOrigAtt->ChannelJoinConfirm(RESULT_TOO_MANY_CHANNELS, uidInitiator, requested_id, 0);
}
}
else
{
/*
* There has been an attempt to join a dynamic channel
* that doesn't already exist. This is not allowed, so
* fail the request.
*/
WARNING_OUT(("Domain::ChannelJoinRequest: attempt to join non-existent dynamic channel"));
pOrigAtt->ChannelJoinConfirm(RESULT_NO_SUCH_CHANNEL, uidInitiator, requested_id, 0);
}
}
else
{
/*
* The channel does not exist locally, and this is not the
* Top Provider. That means this is someone else problem.
* Issue the request upward toward the Top Provider.
*/
TRACE_OUT(("Domain::ChannelJoinRequest: forwarding join request to Top Provider"));
m_pConnToTopProvider->ChannelJoinRequest(uidInitiator, channel_id);
}
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::ChannelJoinRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void ChannelJoinConfirm ()
*
* Public
*
* Functional Description:
* This MCS command originates from a provider who receives a channel
* join request, and has enough information to respond. This is not
* necessarily the Top Provider. An intermediate can respond if the
* channel exists in its information base. This confirm is forwarded
* back to the original requestor, letting it know whether or not the
* join was successful.
*/
Void Domain::ChannelJoinConfirm (
PConnection pOrigConn,
Result result,
UserID uidInitiator,
ChannelID requested_id,
ChannelID channel_id)
{
PChannel channel;
CChannelIDList channel_leave_list;
/*
* Verify that the confirm came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* Make sure that the requesting user is still somewhere in the
* sub-tree of this provider.
*/
if (ValidateUserID (uidInitiator, NULL) )
{
/*
* Found out which downward attachment leads to the requesting
* user.
*/
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
{
CAttachment *pAtt = channel->GetAttachment();
ASSERT(pAtt);
/*
* Was the result successful. If is was, then the local provider
* needs to make sure the channel is in the local channel list.
* If its not already there, it will have to be created.
*/
if (result == RESULT_SUCCESSFUL)
{
/*
* See if the named channel already exists in the channel list.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
/*
* A Channel object already exists for the named channel.
* Let it handle the join confirm.
*/
TRACE_OUT(("Domain::ChannelJoinConfirm: sending confirm to channel object"));
channel->ChannelJoinConfirm(pAtt, result, uidInitiator, requested_id, channel_id);
}
else
{
/*
* The new channel will have to be created.
*/
DBG_SAVE_FILE_LINE
channel = new Channel(channel_id, this, m_pConnToTopProvider,
&m_ChannelList2, &m_AttachmentList);
if (channel != NULL)
{
/*
* Add the newly created channel to the channel list,
* and then let the Channel object handle the join
* confirm.
*/
TRACE_OUT(("Domain::ChannelJoinConfirm: adding channel ID = %04X", (UINT) channel_id));
m_ChannelList2.Insert(channel_id, channel);
Number_Of_Channels++;
channel->ChannelJoinConfirm(pAtt, result, uidInitiator, requested_id, channel_id);
}
else
{
/*
* The allocation of the Channel object failed. It
* is therefore necessary for this provider to cause
* the channel to be deleted from the domain. It
* does this by issuing a channel leave request to
* the Top Provider, and an unsuccessful channel
* join confirm to the originating user.
*/
ERROR_OUT(("Domain::ChannelJoinConfirm: channel allocation failed"));
channel_leave_list.Append(channel_id);
m_pConnToTopProvider->ChannelLeaveRequest(&channel_leave_list);
pAtt->ChannelJoinConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, requested_id, 0);
}
}
}
else
{
/*
* The result was not successful, so this provider does not
* have to worry about creating the channel. It merely
* forwards the join confirm to the originating user.
*/
TRACE_OUT(("Domain::ChannelJoinConfirm: forwarding ChannelJoinConfirm to user"));
pAtt->ChannelJoinConfirm(result, uidInitiator, requested_id, channel_id);
}
}
else
{
ERROR_OUT(("Domain::ChannelJoinConfirm: cannot find the channel"));
}
}
else
{
/*
* The named initiator does not exist in the sub-tree of this
* provider. This could happen if the user is detached before
* the confirm returns. It will be necessary to issue a channel
* leave request upward (if the join was successful).
*/
WARNING_OUT(("Domain::ChannelJoinConfirm: initiator not found"));
if (result == RESULT_SUCCESSFUL)
{
TRACE_OUT(("Domain::ChannelJoinConfirm: sending ChannelLeaveRequest to Top Provider"));
channel_leave_list.Append(channel_id);
m_pConnToTopProvider->ChannelLeaveRequest(&channel_leave_list);
}
}
}
else
{
/*
* This confirm was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::ChannelJoinConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void ChannelLeaveRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially issued by a user that wishes to leave a
* channel. This request will stop cascading upward when it reaches a
* provider that has more attachments joined to the channel than the
* one that is leaving. If the requesting user is the only joined to
* a channel, this request will flow all the way to the Top Provider.
*/
Void Domain::ChannelLeaveRequest (
CAttachment *pOrigAtt,
CChannelIDList *channel_id_list)
{
ChannelID chid;
PChannel channel;
CChannelIDList channel_leave_list;
/*
* Make sure that the attachment leaving the channel really does exist.
*/
if (m_AttachmentList.Find(pOrigAtt))
{
/*
* Iterate through the list of channels to be left, processing each
* one independently.
*/
channel_id_list->Reset();
while (NULL != (chid = channel_id_list->Iterate()))
{
/*
* Check to make sure that the channel being left really does
* exist.
*/
if (NULL != (channel = m_ChannelList2.Find(chid)))
{
/*
* Let the Channel object deal with this request. After
* sending the leave request to the channel, it is necessary to
* check the validity of the channel object determine if it
* should be deleted as a result of this leave operation.
*/
TRACE_OUT(("Domain::ChannelLeaveRequest: processing leave request for channel ID = %04X", (UINT) chid));
channel_leave_list.Clear();
channel_leave_list.Append(chid);
channel->ChannelLeaveRequest(pOrigAtt, &channel_leave_list);
if (channel->IsValid () == FALSE)
DeleteChannel(chid);
}
else
{
/*
* The named channel does not exist in the information base.
* Ignore the request.
*/
WARNING_OUT(("Domain::ChannelLeaveRequest: received leave request for non-existent channel"));
}
}
}
else
{
/*
* This request originated from an attachment that does not exist
* in the sub-tree of this provider.
*/
ERROR_OUT(("Domain::ChannelLeaveRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void ChannelConveneRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by a user that wishes to convene a
* new private channel. It is forwarded upward to the Top Provider who
* will attempt to create the private channel.
*/
Void Domain::ChannelConveneRequest (
CAttachment *pOrigAtt,
UserID uidInitiator)
{
ChannelID channel_id;
PChannel channel;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* If this is the Top Provider, then the request can be serviced
* locally. If not, then it must be forwarded upward.
*/
if (IsTopProvider())
{
/*
* See if the arbitrated domain parameters will allow the
* addition of a new channel.
*/
if (Number_Of_Channels < Domain_Parameters.max_channel_ids)
{
/*
* Since this is a request for a private channel, it is
* necessary to allocate a channel ID from the dynamic range.
* Then, create the private channel.
*/
channel_id = AllocateDynamicChannel ();
DBG_SAVE_FILE_LINE
channel = new PrivateChannel(channel_id, uidInitiator, this, m_pConnToTopProvider,
&m_ChannelList2, &m_AttachmentList);
if (channel != NULL)
{
/*
* The creation of the new private channel was successful.
* Add it to the channel list. Note that the channel
* object itself will issue the channel convene confirm.
*/
TRACE_OUT(("Domain::ChannelConveneRequest: adding channel ID = %04X", (UINT) channel_id));
m_ChannelList2.Insert(channel_id, channel);
Number_Of_Channels++;
}
else
{
/*
* Allocation of the PrivateChannel object failed. We
* must therefore issue an unsuccessful channel
* convene confirm to the originating attachment.
*/
ERROR_OUT(("Domain::ChannelConveneRequest: channel allocation failed"));
pOrigAtt->ChannelConveneConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, 0);
}
}
else
{
/*
* Domain parmeters will not allow the addition of
* any more channels. Fail the request.
*/
ERROR_OUT(("Domain::ChannelConveneRequest: join denied - too many channels"));
pOrigAtt->ChannelConveneConfirm(RESULT_TOO_MANY_CHANNELS, uidInitiator, 0);
}
}
else
{
/*
* This is not the Top Provider. That means this is someone elses
* problem. Issue the request upward toward the Top Provider.
*/
TRACE_OUT(("Domain::ChannelConveneRequest: forwarding convene request to Top Provider"));
m_pConnToTopProvider->ChannelConveneRequest(uidInitiator);
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::ChannelConveneRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void ChannelConveneConfirm ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the Top Provider in response to
* a previously received ChannelConveneRequest. This command contains the
* results of the request.
*/
Void Domain::ChannelConveneConfirm (
PConnection pOrigConn,
Result result,
UserID uidInitiator,
ChannelID channel_id)
{
PChannel channel;
/*
* Verify that the confirm came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* Make sure that the requesting user is still somewhere in the
* sub-tree of this provider.
*/
if (ValidateUserID (uidInitiator, NULL) )
{
/*
* Found out which downward attachment leads to the requesting
* user.
*/
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
{
CAttachment *pAtt = channel->GetAttachment();
ASSERT(pAtt);
/*
* Was the result successful. If is was, then the local provider
* needs to create the new private channel in the local information
* base.
*/
if (result == RESULT_SUCCESSFUL)
{
/*
* See if the named channel already exists in the channel list.
* Note that it is an error to receive a channel convene
* confirm for a channel that already exists. This would
* indicate a logic error somewhere in the domain hierarchy
* above this provider.
*/
if (! m_ChannelList2.Find(channel_id))
{
/*
* The new private channel has to be created.
*/
DBG_SAVE_FILE_LINE
channel = new PrivateChannel(channel_id, uidInitiator, this, m_pConnToTopProvider,
&m_ChannelList2, &m_AttachmentList);
if (channel != NULL)
{
/*
* Add the newly created channel to the channel list.
* Let the Channel object handle the convene confirm.
*/
TRACE_OUT(("Domain::ChannelConveneConfirm: adding channel ID = %04X", (UINT) channel_id));
m_ChannelList2.Insert(channel_id, channel);
Number_Of_Channels++;
}
else
{
/*
* The allocation of the Channel object failed. It
* is therefore necessary for this provider to cause
* the channel to be deleted from the domain. It
* does this by issuing a channel disband request to
* the Top Provider, and an unsuccessful channel
* convene confirm to the originating user.
*/
ERROR_OUT(("Domain::ChannelConveneConfirm: channel allocation failed"));
m_pConnToTopProvider->ChannelDisbandRequest(uidInitiator, channel_id);
pAtt->ChannelConveneConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, 0);
}
}
else
{
/*
* A Channel object already exists for the named channel.
* This is an error, so report the problem, and ignore
* the confirm.
*/
ERROR_OUT(("Domain::ChannelConveneConfirm: channel already exists in channel list"));
}
}
else
{
/*
* The result was not successful, so this provider does not
* have to worry about creating the channel. It merely
* forwards the join confirm to the originating user.
*/
TRACE_OUT(("Domain::ChannelConveneConfirm: forwarding ChannelConveneConfirm to user"));
pAtt->ChannelConveneConfirm(result, uidInitiator, channel_id);
}
}
else
{
ERROR_OUT(("Domain::ChannelConveneConfirm: cannot find the channel"));
}
}
else
{
/*
* The named initiator does not exist in the sub-tree of this
* provider. This could happen if the user is detached before
* the confirm returns. Note that since a DetachUserIndication
* will automatically be issued upward for the lost channel
* manager, it is unnecessary for this provider to take any
* special action to eliminate the unowned private channel.
*/
ERROR_OUT(("Domain::ChannelConveneConfirm: initiator not found"));
}
}
else
{
/*
* This confirm was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::ChannelConveneConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void ChannelDisbandRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by a user that wishes to disband a
* private channel that it previously created. If the channel is in the
* local information base, the request is sent to it. Otherwise, the
* request is ignored.
*/
Void Domain::ChannelDisbandRequest (
CAttachment *pOrigAtt,
UserID uidInitiator,
ChannelID channel_id)
{
PChannel channel;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* Check to see if the specified channel exists in the Channel List.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
/*
* Send the disband request to the channel object to handle it.
* Then ask the channel object if this request has resulted in a
* need for the channel to be deleted. This will occur when the
* disband request is handled at the Top Provider.
*/
if (channel->GetChannelType() == PRIVATE_CHANNEL)
{
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
TRACE_OUT(("Domain::ChannelDisbandRequest: sending disband request to channel object"));
pPrivChnl->ChannelDisbandRequest(pOrigAtt, uidInitiator, channel_id);
}
else
{
ERROR_OUT(("Domain::ChannelDisbandRequest: it should be private chanel"));
}
if (channel->IsValid () == FALSE)
DeleteChannel (channel_id);
}
else
{
/*
* The channel does not exist in the information base. That means
* that this request is invalid, and should be ignored.
*/
ERROR_OUT(("Domain::ChannelDisbandRequest: channel does not exist"));
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::ChannelDisbandRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void ChannelDisbandIndication ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the Top Provider when it decides
* to delete a private channel from the domain. It travels downward to
* all attachments and connections that contain an admitted user or the
* channel manager in their sub-tree.
*/
Void Domain::ChannelDisbandIndication (
PConnection pOrigConn,
ChannelID channel_id)
{
PChannel channel;
/*
* Verify that the indication came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* Check to see if the specified channel exists in the Channel List.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
/*
* Send the disband indication to the channel object to handle it.
* Then delete the object from the local information base, as it is
* no longer needed.
*/
if (channel->GetChannelType() == PRIVATE_CHANNEL)
{
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
TRACE_OUT(("Domain::ChannelDisbandIndication: sending disband indication to channel object"));
pPrivChnl->ChannelDisbandIndication(channel_id);
}
else
{
ERROR_OUT(("Domain::ChannelDisbandIndication: it should be private chanel"));
}
if (channel->IsValid () == FALSE)
DeleteChannel (channel_id);
}
else
{
/*
* The channel does not exist in the information base. That means
* that this indication is invalid, and should be ignored.
*/
ERROR_OUT(("Domain::ChannelDisbandIndication: channel does not exist"));
}
}
else
{
/*
* This indication was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::ChannelDisbandIndication: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void ChannelAdmitRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the manager of a private channel
* when it wishes to expand the authorized user list of that channel. If
* the channel is in the local information base, the request is sent to it.
* Otherwise, the request is ignored.
*/
Void Domain::ChannelAdmitRequest (
CAttachment *pOrigAtt,
UserID uidInitiator,
ChannelID channel_id,
CUidList *user_id_list)
{
PChannel channel;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* Check to see if the specified channel exists in the Channel List.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
if (channel->GetChannelType() == PRIVATE_CHANNEL)
{
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
/*
* Send the admit request to the channel object to handle it.
*/
TRACE_OUT(("Domain::ChannelAdmitRequest: sending admit request to channel object"));
pPrivChnl->ChannelAdmitRequest(pOrigAtt, uidInitiator, channel_id, user_id_list);
}
else
{
ERROR_OUT(("Domain::ChannelAdmitRequest: it should be private chanel"));
}
}
else
{
/*
* The channel does not exist in the information base. That means
* that this request is invalid, and should be ignored.
*/
ERROR_OUT(("Domain::ChannelAdmitRequest: channel does not exist"));
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::ChannelAdmitRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void ChannelAdmitIndication ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the Top Provider when it receives
* a channel admit indication from the manager of a private channel. This
* indication is broadcast downward to all providers that contain an
* admitted user somewhere in their sub-tree. A side-effect of this
* indication is that a private channel will be created in the information
* base if one does not already exist.
*/
Void Domain::ChannelAdmitIndication (
PConnection pOrigConn,
UserID uidInitiator,
ChannelID channel_id,
CUidList *user_id_list)
{
PChannel channel;
/*
* Verify that the indication came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* Check to see if the specified channel exists in the Channel List.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
if (channel->GetChannelType() == PRIVATE_CHANNEL)
{
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
/*
* Send the admit indication to the channel object to handle it.
*/
TRACE_OUT(("Domain::ChannelAdmitIndication: sending admit indication to channel object"));
pPrivChnl->ChannelAdmitIndication(pOrigConn, uidInitiator, channel_id, user_id_list);
}
else
{
ERROR_OUT(("Domain::ChannelAdmitIndication: it should be private chanel"));
}
}
else
{
/*
* Since the private channel does not exist in the information
* base, it will be necessary to create one. After it is created,
* it can handle the channel admit indication.
*/
DBG_SAVE_FILE_LINE
channel = new PrivateChannel(channel_id, uidInitiator, this, m_pConnToTopProvider,
&m_ChannelList2, &m_AttachmentList);
if (channel != NULL)
{
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
/*
* Put the newly created private channel into the domain
* information base.
*/
TRACE_OUT(("Domain::ChannelAdmitIndication: adding channel ID = %04X", (UINT) channel_id));
m_ChannelList2.Insert(channel_id, channel);
Number_Of_Channels++;
/*
* Send the admit indication to the new channel object to
* handle it.
*/
pPrivChnl->ChannelAdmitIndication(pOrigConn, uidInitiator, channel_id, user_id_list);
}
else
{
/*
* We have been told by the Top Provider to create a private
* channel, but we can't due to a resource shortage. We also
* can't purge the channel from the domain since the channel
* manager does not exist in the sub-tree of this provider.
* We are therefore out of sync with the Top Provider, and
* there is nothing we can do about it (except for possibly
* disconnecting from the Top Provider and purging the entire
* domain from this node downward).
*/
ERROR_OUT(("Domain::ChannelAdmitIndication: channel allocation failure"));
}
}
}
else
{
/*
* This indication was received from someone besides the Top Provider.
* Ignore the indication.
*/
WARNING_OUT(("Domain::ChannelAdmitIndication: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void ChannelExpelRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the manager of a private channel
* when it wishes to shrink the authorized user list of that channel. If
* the channel is in the local information base, the request is sent to it.
* Otherwise, the request is ignored.
*/
Void Domain::ChannelExpelRequest (
CAttachment *pOrigAtt,
UserID uidInitiator,
ChannelID channel_id,
CUidList *user_id_list)
{
PChannel channel;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* Check to see if the specified channel exists in the Channel List.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
if (channel->GetChannelType() == PRIVATE_CHANNEL)
{
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
/*
* Send the admit request to the channel object to handle it.
*/
TRACE_OUT(("Domain::ChannelExpelRequest: "
"sending expel request to channel object"));
pPrivChnl->ChannelExpelRequest(pOrigAtt, uidInitiator, channel_id, user_id_list);
}
else
{
ERROR_OUT(("Domain::ChannelExpelRequest: it should be private chanel"));
}
}
else
{
/*
* The channel does not exist in the information base. That means
* that this request is invalid, and should be ignored.
*/
WARNING_OUT(("Domain::ChannelExpelRequest: channel does not exist"));
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
WARNING_OUT(("Domain::ChannelExpelRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void ChannelExpelIndication ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the Top Provider when it receives
* a request from the manager of a private channel to reduce the
* authorized user list. It travels downward to all attachments and
* connections that contain an admitted user or the channel manager in
* their sub-tree.
*/
Void Domain::ChannelExpelIndication (
PConnection pOrigConn,
ChannelID channel_id,
CUidList *user_id_list)
{
PChannel channel;
/*
* Verify that the indication came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* Check to see if the specified channel exists in the Channel List.
*/
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
{
if (channel->GetChannelType() == PRIVATE_CHANNEL)
{
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
/*
* Send the expel indication to the channel object to handle it.
* Then check to see if the channel is still valid (delete it
* if not). This would occur if the expel results in an empty
* admitted user list, and the channel manager is also not in the
* sub-tree of this provider.
*/
TRACE_OUT(("Domain::ChannelExpelIndication: sending expel indication to channel object"));
pPrivChnl->ChannelExpelIndication(pOrigConn, channel_id, user_id_list);
}
else
{
ERROR_OUT(("Domain::ChannelExpelIndication: it should be private chanel"));
}
if (channel->IsValid () == FALSE)
DeleteChannel (channel_id);
}
else
{
/*
* The channel does not exist in the information base. That means
* that this indication is invalid, and should be ignored.
*/
ERROR_OUT(("Domain::ChannelExpelIndication: channel does not exist"));
}
}
else
{
/*
* This indication was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::ChannelExpelIndication: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void SendDataRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially issued by a user attachment that wishes
* to send data on a particular channel in this domain. The request will
* flow upward all the way to the Top Provider. It will also cause
* send data indications to be sent downward to all other attachments
* that are joined to the channel.
*/
Void Domain::SendDataRequest (
CAttachment *pOrigAtt,
UINT type,
PDataPacket data_packet)
{
PChannel channel;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(data_packet->GetInitiator(), pOrigAtt))
{
/*
* See if the channel exists in the local information base. If it does
* then let the Channel object handle the routing of the data. If
* it does not exist, then simply forward the request upward to be
* handled by the next higher provider (unless this is the Top
* Provider).
*/
if (NULL != (channel = m_ChannelList2.Find(data_packet->GetChannelID())))
channel->SendDataRequest(pOrigAtt, type, data_packet);
else if (! IsTopProvider())
m_pConnToTopProvider->SendDataRequest(data_packet);
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
WARNING_OUT (("Domain::SendDataRequest: invalid originator=0x%p, uidInitiator=%d", pOrigAtt, data_packet->GetInitiator()));
}
}
/*
* Void SendDataIndication ()
*
* Public
*
* Functional Description:
* This MCS command is issued by all providers that receive a send data
* request on a channel to which one of their attachments is joined. It
* delivers data in a non-uniform fashion to all users joined to the
* named channel.
*/
Void Domain::SendDataIndication (
PConnection pOrigConn,
UINT type,
PDataPacket data_packet)
{
PChannel channel;
/*
* Verify that the indication came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* See if the channel exists in the local information base. If it does
* then let the Channel object handle the routing of the data. If
* it does not exist, then ignore the request.
*/
if (NULL != (channel = m_ChannelList2.Find(data_packet->GetChannelID())))
channel->SendDataIndication(pOrigConn, type, data_packet);
}
else
{
/*
* This indication was received from someone besides the Top Provider.
* Ignore the indication.
*/
WARNING_OUT (("Domain::SendDataIndication: invalid originator=0x%p, initiator=%d", pOrigConn, data_packet->GetInitiator()));
}
}
/*
* Void TokenGrabRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by a user attachment that wishes
* to grab a token. It flows upward to the Top Provider, who attempts
* to satisfy the request.
*/
Void Domain::TokenGrabRequest (
CAttachment *pOrigAtt,
UserID uidInitiator,
TokenID token_id)
{
PToken token;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* If the token already exists in the token list, then let the Token
* object deal with the request.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
TRACE_OUT(("Domain::TokenGrabRequest: sending grab request to token object"));
token->TokenGrabRequest(pOrigAtt, uidInitiator, token_id);
}
else
{
/*
* The token does not exist yet. Check to see if this is the Top
* Provider. If it is, then the request can be processed locally.
* Otherwise, forward the request upward.
*/
if (IsTopProvider())
{
/*
* Check to see if the arbitrated domain parameters will allow
* the addition of another token.
*/
if (Number_Of_Tokens < Domain_Parameters.max_token_ids)
{
/*
* Try to create a new Token object.
*/
DBG_SAVE_FILE_LINE
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
if (token != NULL)
{
/*
* Put the newly created Token object into the token
* list. Then pass the grab request to it.
*/
TRACE_OUT(("Domain::TokenGrabRequest: adding token ID = %04X", (UINT) token_id));
m_TokenList2.Append(token_id, token);
Number_Of_Tokens++;
token->TokenGrabRequest(pOrigAtt, uidInitiator, token_id);
}
else
{
/*
* The allocation of the Token object failed. It is
* therefore necessary to fail the request.
*/
ERROR_OUT(("Domain::TokenGrabRequest: token allocation failed"));
pOrigAtt->TokenGrabConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, token_id, TOKEN_NOT_IN_USE);
}
}
else
{
/*
* The arbitrated domain parameters will not allow the
* creation of another token in this domain. So fail
* the request.
*/
ERROR_OUT(("Domain::TokenGrabRequest: grab denied - too many tokens"));
pOrigAtt->TokenGrabConfirm(RESULT_TOO_MANY_TOKENS, uidInitiator, token_id, TOKEN_NOT_IN_USE);
}
}
else
{
/*
* This is not the Top Provider. Forward the request upward.
*/
TRACE_OUT(("Domain::TokenGrabRequest: forwarding grab request to Top Provider"));
m_pConnToTopProvider->TokenGrabRequest(uidInitiator, token_id);
}
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::TokenGrabRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void TokenGrabConfirm ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the Top Provider upon receipt of
* a grab request. It is sent back to the initiating user, containing
* the result of the request.
*/
Void Domain::TokenGrabConfirm (
PConnection pOrigConn,
Result result,
UserID uidInitiator,
TokenID token_id,
TokenStatus token_status)
{
PToken token;
/*
* Verify that the confirm came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* See if the token already exists in the local information base. If
* so, let it handle this.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
TRACE_OUT(("Domain::TokenGrabConfirm: sending grab confirm to token object"));
token->TokenGrabConfirm(result, uidInitiator, token_id, token_status);
}
else
{
PChannel channel;
/*
* Make sure that the requesting user is still somewhere in the
* sub-tree of this provider.
*/
if (ValidateUserID (uidInitiator, NULL) )
{
/*
* Determine which attachment leads to the initiating user.
*/
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
{
CAttachment *pAtt = channel->GetAttachment();
ASSERT(pAtt);
/*
* If the result of the request is successful, then it is
* necessary to create the token in the local information base.
*/
if (result == RESULT_SUCCESSFUL)
{
/*
* Create the token.
*/
DBG_SAVE_FILE_LINE
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
if (token != NULL)
{
/*
* Put the newly created Token object into the token
* list. Then pass the grab confirm to it.
*/
TRACE_OUT(("Domain::TokenGrabConfirm: adding token ID = %04X", (UINT) token_id));
m_TokenList2.Append(token_id, token);
Number_Of_Tokens++;
token->TokenGrabConfirm(result, uidInitiator, token_id, token_status);
}
else
{
/*
* The creation of the token failed. It is therefore
* necessary to send a failed confirm to the initiating
* user, as well as a token release request to the Top
* Provider.
*/
ERROR_OUT(("Domain::TokenGrabConfirm: token creation failed"));
m_pConnToTopProvider->TokenReleaseRequest(uidInitiator, token_id);
pAtt->TokenGrabConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, token_id, TOKEN_NOT_IN_USE);
}
}
else
{
/*
* The confirm was unsuccessful, so there is no need to
* create a token in the information base. Just forward
* the confirm to the initiating user.
*/
TRACE_OUT(("Domain::TokenGrabConfirm: forwarding failed grab confirm"));
pAtt->TokenGrabConfirm(result, uidInitiator, token_id, token_status);
}
}
else
{
ERROR_OUT(("Domain::TokenGrabConfirm: cannot find channel"));
}
}
else
{
/*
* The named initiator does not exist in the sub-tree of this
* provider. Ignore the confirm.
*/
ERROR_OUT(("Domain::TokenGrabConfirm: invalid initiator, uidInitiator=%u", (UINT) uidInitiator));
}
}
}
else
{
/*
* This confirm was received from someone besides the Top Provider.
* Ignore the confirm.
*/
ERROR_OUT(("Domain::TokenGrabConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void TokenInhibitRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by a user attachment that wishes
* to inhibit a token. It flows upward to the Top Provider, who attempts
* to satisfy the request.
*/
Void Domain::TokenInhibitRequest (
CAttachment *pOrigAtt,
UserID uidInitiator,
TokenID token_id)
{
PToken token;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* If the token already exists in the token list, then let the Token
* object deal with the request.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
TRACE_OUT(("Domain::TokenInhibitRequest: sending inhibit request to token object"));
token->TokenInhibitRequest(pOrigAtt, uidInitiator, token_id);
}
else
{
/*
* The token does not exist yet. Check to see if this is the Top
* Provider. If it is, then the request can be processed locally.
* Otherwise, forward the request upward.
*/
if (IsTopProvider())
{
/*
* Check to see if the arbitrated domain parameters will allow
* the addition of another token.
*/
if (Number_Of_Tokens < Domain_Parameters.max_token_ids)
{
/*
* Try to create a new Token object.
*/
DBG_SAVE_FILE_LINE
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
if (token != NULL)
{
/*
* Put the newly created Token object into the token
* list. Then pass the inhibit request to it.
*/
TRACE_OUT(("Domain::TokenInhibitRequest: adding token ID = %04X", (UINT) token_id));
m_TokenList2.Append(token_id, token);
Number_Of_Tokens++;
token->TokenInhibitRequest(pOrigAtt, uidInitiator, token_id);
}
else
{
/*
* The allocation of the Token object failed. It is
* therefore necessary to fail the request.
*/
ERROR_OUT(("Domain::TokenInhibitRequest: token allocation failed"));
pOrigAtt->TokenInhibitConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, token_id, TOKEN_NOT_IN_USE);
}
}
else
{
/*
* The arbitrated domain parameters will not allow the
* creation of another token in this domain. So fail
* the request.
*/
ERROR_OUT(("Domain::TokenInhibitRequest: inhibit denied - too many tokens"));
pOrigAtt->TokenInhibitConfirm(RESULT_TOO_MANY_TOKENS, uidInitiator, token_id, TOKEN_NOT_IN_USE);
}
}
else
{
/*
* This is not the Top Provider. Forward the request upward.
*/
TRACE_OUT(("Domain::TokenInhibitRequest: forwarding inhibit request to Top Provider"));
m_pConnToTopProvider->TokenInhibitRequest(uidInitiator, token_id);
}
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::TokenInhibitRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void TokenInhibitConfirm ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the Top Provider upon receipt of
* a inhibit request. It is sent back to the initiating user, containing
* the result of the request.
*/
Void Domain::TokenInhibitConfirm (
PConnection pOrigConn,
Result result,
UserID uidInitiator,
TokenID token_id,
TokenStatus token_status)
{
PToken token;
/*
* Verify that the confirm came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* See if the token already exists in the local information base. If
* so, let it handle this.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
TRACE_OUT(("Domain::TokenInhibitConfirm: sending inhibit confirm to token object"));
token->TokenInhibitConfirm(result, uidInitiator, token_id, token_status);
}
else
{
PChannel channel;
/*
* Make sure that the requesting user is still somewhere in the
* sub-tree of this provider.
*/
if (ValidateUserID (uidInitiator, NULL) )
{
/*
* Determine which attachment leads to the requesting user.
*/
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
{
CAttachment *pAtt = channel->GetAttachment();
ASSERT(pAtt);
/*
* If the result of the request is successful, then it is
* necessary to create the token in the local information base.
*/
if (result == RESULT_SUCCESSFUL)
{
/*
* Create the token.
*/
DBG_SAVE_FILE_LINE
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
if (token != NULL)
{
/*
* Put the newly created Token object into the token
* list. Then pass the inhibit confirm to it.
*/
TRACE_OUT(("Domain::TokenInhibitConfirm: adding token ID = %04X", (UINT) token_id));
m_TokenList2.Append(token_id, token);
Number_Of_Tokens++;
token->TokenInhibitConfirm(result, uidInitiator, token_id, token_status);
}
else
{
/*
* The creation of the token failed. It is therefore
* necessary to send a failed confirm to the initiating
* user, as well as a token release request to the Top
* Provider.
*/
ERROR_OUT(("Domain::TokenInhibitConfirm: token creation failed"));
m_pConnToTopProvider->TokenReleaseRequest(uidInitiator, token_id);
pAtt->TokenInhibitConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, token_id, TOKEN_NOT_IN_USE);
}
}
else
{
/*
* The confirm was unsuccessful, so there is no need to
* create a token in the information base. Just forward
* the confirm to the initiating user.
*/
ERROR_OUT(("Domain::TokenInhibitConfirm: forwarding failed inhibit confirm"));
pAtt->TokenInhibitConfirm(result, uidInitiator, token_id, token_status);
}
}
else
{
ERROR_OUT(("Domain::TokenInhibitConfirm: cannot find channel"));
}
}
else
{
/*
* The named initiator does not exist in the sub-tree of this
* provider. Ignore the confirm.
*/
ERROR_OUT(("Domain::TokenInhibitConfirm: initiator not valid, uidInitiator=%u", (UINT) uidInitiator));
}
}
}
else
{
/*
* This confirm was received from someone besides the Top Provider.
* Ignore the confirm.
*/
ERROR_OUT(("Domain::TokenInhibitConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void TokenGiveRequest ()
*
* Public
*
* Functional Description:
*/
Void Domain::TokenGiveRequest (
CAttachment *pOrigAtt,
PTokenGiveRecord pTokenGiveRec)
{
PToken token;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(pTokenGiveRec->uidInitiator, pOrigAtt))
{
/*
* If the token already exists in the token list, then let the
* Token object deal with the request.
*/
if (NULL != (token = m_TokenList2.Find(pTokenGiveRec->token_id)))
{
TRACE_OUT(("Domain::TokenGiveRequest: sending give request to token object"));
token->TokenGiveRequest(pOrigAtt, pTokenGiveRec);
}
else
{
/*
* Check to see if this is the Top Provider. If it is, then the
* request can be processed locally. Otherwise, forward the
* request upward.
*/
if (IsTopProvider())
{
/*
* The token does not exist in this domain. Report this and
* send the appropriate give confirm back to the originating
* user.
*/
ERROR_OUT(("Domain::TokenGiveRequest: token does not exist"));
pOrigAtt->TokenGiveConfirm(RESULT_TOKEN_NOT_POSSESSED,
pTokenGiveRec->uidInitiator, pTokenGiveRec->token_id, TOKEN_NOT_IN_USE);
}
else
{
/*
* This is not the Top Provider. Forward the request upward.
*/
TRACE_OUT(("Domain::TokenGiveRequest: forwarding give request to Top Provider"));
m_pConnToTopProvider->TokenGiveRequest(pTokenGiveRec);
}
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::TokenGiveRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void TokenGiveIndication ()
*
* Public
*
* Functional Description:
*/
Void Domain::TokenGiveIndication (
PConnection pOrigConn,
PTokenGiveRecord pTokenGiveRec)
{
PToken token;
TokenID token_id = pTokenGiveRec->token_id;
/*
* Verify that the indication came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* See if the token already exists in the local information base. If
* so, let it handle this.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
TRACE_OUT(("Domain::TokenGiveIndication: sending give indication to token object"));
token->TokenGiveIndication(pTokenGiveRec);
}
else
{
/*
* Make sure that the specified receiver is somewhere in the
* sub-tree of this provider.
*/
if (ValidateUserID (pTokenGiveRec->receiver_id, NULL) )
{
/*
* Create the token.
*/
DBG_SAVE_FILE_LINE
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
if (token != NULL)
{
/*
* Put the newly created Token object into the token
* list. Then pass the give indication to it.
*/
TRACE_OUT(("Domain::TokenGiveIndication: adding token ID = %04X", (UINT) token_id));
m_TokenList2.Append(token_id, token);
Number_Of_Tokens++;
token->TokenGiveIndication(pTokenGiveRec);
}
else
{
/*
* The creation of the token failed. It is therefore
* necessary to send a failed give response to the Top
* Provider.
*/
ERROR_OUT(("Domain::TokenGiveIndication: token creation failed"));
m_pConnToTopProvider->TokenGiveResponse(RESULT_UNSPECIFIED_FAILURE,
pTokenGiveRec->uidInitiator, token_id);
}
}
else
{
/*
* The specified receiver does not exist in the sub-tree of
* this provider. It is not necessary for this provider to
* take special action, since the detach user indication for
* the receiver will clean up.
*/
ERROR_OUT(("Domain::TokenGiveIndication: receiver not valid"));
}
}
}
else
{
/*
* This indication was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::TokenGiveIndication: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void TokenGiveResponse ()
*
* Public
*
* Functional Description:
*/
Void Domain::TokenGiveResponse (
CAttachment *pOrigAtt,
Result result,
UserID receiver_id,
TokenID token_id)
{
PToken token;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this response originated.
*/
if (ValidateUserID(receiver_id, pOrigAtt))
{
/*
* If the token already exists in the token list, then let the
* Token object deal with the response.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
/*
* Send the give response to the token object. Then check to
* see if it is still valid (delete it if not).
*/
TRACE_OUT(("Domain::TokenGiveResponse: sending give response to token object"));
token->TokenGiveResponse(result, receiver_id, token_id);
if (token->IsValid () == FALSE)
DeleteToken (token_id);
}
else
{
/*
* The token is not in the information base, which means that it
* cannot be being given to the initiator of this response.
* Ignore the response.
*/
ERROR_OUT(("Domain::TokenGiveResponse: no such token"));
}
}
else
{
/*
* There is no such user in the sub-tree from which this response
* originated. Ignore the response.
*/
ERROR_OUT(("Domain::TokenGiveResponse: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void TokenGiveConfirm ()
*
* Public
*
* Functional Description:
*/
Void Domain::TokenGiveConfirm (
PConnection pOrigConn,
Result result,
UserID uidInitiator,
TokenID token_id,
TokenStatus token_status)
{
PToken token;
/*
* Verify that the confirm came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* See if the token already exists in the local information base. If
* so, let it handle this.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
/*
* Send the give confirm to the token object. Then check to
* see if it is still valid (delete it if not).
*/
TRACE_OUT(("Domain::TokenGiveConfirm: sending give confirm to token object"));
token->TokenGiveConfirm(result, uidInitiator, token_id, token_status);
if (token->IsValid () == FALSE)
DeleteToken (token_id);
}
else
{
/*
* Make sure that the requesting user is still somewhere in the
* sub-tree of this provider.
*/
if (ValidateUserID (uidInitiator, NULL) )
{
PChannel channel;
/*
* Determine which attachment leads to the requesting user.
* Then forward the confirm in that direction.
*/
TRACE_OUT(("Domain::TokenGiveConfirm: forwarding give confirm"));
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
{
CAttachment *pAtt = channel->GetAttachment();
if (pAtt)
{
pAtt->TokenGiveConfirm(result, uidInitiator, token_id, token_status);
}
else
{
ERROR_OUT(("Domain::TokenGiveConfirm: cannot get attachment"));
}
}
else
{
ERROR_OUT(("Domain::TokenGiveConfirm: cannot find channel"));
}
}
else
{
/*
* The named initiator does not exist in the sub-tree of this
* provider. Ignore the confirm.
*/
ERROR_OUT(("Domain::TokenGiveConfirm: initiator not valid"));
}
}
}
else
{
/*
* This confirm was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::TokenGiveConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void TokenPleaseRequest ()
*
* Public
*
* Functional Description:
*/
Void Domain::TokenPleaseRequest (
CAttachment *pOrigAtt,
UserID uidInitiator,
TokenID token_id)
{
PToken token;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* If the token already exists in the token list, then let the
* Token object deal with the request.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
TRACE_OUT(("Domain::TokenPleaseRequest: sending please request to token object"));
token->TokenPleaseRequest(uidInitiator, token_id);
}
else
{
/*
* Check to see if this is the Top Provider. If it is, then the
* request can be processed locally. Otherwise, forward the
* request upward.
*/
if (IsTopProvider())
{
/*
* The token being released is not owned by anyone. Report the
* incident to the diagnostic window, but do nothing. This
* simply indicates that someone has issued a please request
* for a token that no one owns.
*/
ERROR_OUT(("Domain::TokenPleaseRequest: token does not exist"));
}
else
{
/*
* This is not the Top Provider. Forward the request upward.
*/
TRACE_OUT(("Domain::TokenPleaseRequest: forwarding please request to Top Provider"));
m_pConnToTopProvider->TokenPleaseRequest(uidInitiator, token_id);
}
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::TokenPleaseRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void TokenPleaseIndication ()
*
* Public
*
* Functional Description:
*/
Void Domain::TokenPleaseIndication (
PConnection pOrigConn,
UserID uidInitiator,
TokenID token_id)
{
PToken token;
/*
* Verify that the indication came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* See if the token already exists in the local information base. If
* so, let it handle this.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
TRACE_OUT(("Domain::TokenPleaseIndication: sending please indication to token object"));
token->TokenPleaseIndication(uidInitiator, token_id);
}
else
{
/*
* Since token please indication is only sent downward to providers
* that have owners in their sub-tree, it should not be possible
* to get here. This indicates that this provider received the
* indication with NO owners in its sub-tree. Report the error
* and ignore the indication.
*/
ERROR_OUT(("Domain::TokenPleaseIndication: invalid token"));
}
}
else
{
/*
* This indication was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::TokenPleaseIndication: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void TokenReleaseRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by a user attachment that wishes
* to release a token. It flows upward to the Top Provider, who attempts
* to satisfy the request.
*/
Void Domain::TokenReleaseRequest (
CAttachment *pOrigAtt,
UserID uidInitiator,
TokenID token_id)
{
PToken token;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* If the token already exists in the token list, then let the
* Token object deal with the request.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
/*
* Send the release request to the token object. Then check to
* see if it is still valid (delete it if not).
*/
TRACE_OUT(("Domain::TokenReleaseRequest: sending release request to token object"));
token->TokenReleaseRequest(pOrigAtt, uidInitiator, token_id);
if (token->IsValid () == FALSE)
DeleteToken (token_id);
}
else
{
/*
* Check to see if this is the Top Provider. If it is, then the
* request can be processed locally. Otherwise, forward the
* request upward.
*/
if (IsTopProvider())
{
/*
* The token being released is not owned by anyone. Return
* a failure to the initiating user.
*/
ERROR_OUT(("Domain::TokenReleaseRequest: token does not exist"));
pOrigAtt->TokenReleaseConfirm(RESULT_TOKEN_NOT_POSSESSED, uidInitiator, token_id, TOKEN_NOT_IN_USE);
}
else
{
/*
* This is not the Top Provider. Forward the request upward.
*/
TRACE_OUT(("Domain::TokenReleaseRequest: forwarding release request to Top Provider"));
m_pConnToTopProvider->TokenReleaseRequest(uidInitiator, token_id);
}
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::TokenReleaseRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void TokenReleaseConfirm ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the Top Provider upon receipt of
* a release request. It is sent back to the initiating user, containing
* the result of the request.
*/
Void Domain::TokenReleaseConfirm (
PConnection pOrigConn,
Result result,
UserID uidInitiator,
TokenID token_id,
TokenStatus token_status)
{
PToken token;
/*
* Verify that the confirm came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* See if the token already exists in the local information base. If
* so, let it handle this.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
/*
* Send the release confirm to the token object. Then check to
* see if it is still valid (delete it if not).
*/
TRACE_OUT(("Domain::TokenReleaseConfirm: sending release confirm to token object"));
token->TokenReleaseConfirm(result, uidInitiator, token_id, token_status);
if (token->IsValid () == FALSE)
DeleteToken (token_id);
}
else
{
/*
* Make sure that the requesting user is still somewhere in the
* sub-tree of this provider.
*/
if (ValidateUserID (uidInitiator, NULL) )
{
PChannel channel;
/*
* Determine which attachment leads to the requesting user.
* Then forward the confirm in that direction.
*/
TRACE_OUT(("Domain::TokenReleaseConfirm: forwarding release confirm"));
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
{
CAttachment *pAtt = channel->GetAttachment();
if (pAtt)
{
pAtt->TokenReleaseConfirm(result, uidInitiator, token_id, token_status);
}
else
{
ERROR_OUT(("Domain::TokenReleaseConfirm: cannot get attachment"));
}
}
else
{
ERROR_OUT(("Domain::TokenReleaseConfirm: cannot find channel"));
}
}
else
{
/*
* The named initiator does not exist in the sub-tree of this
* provider. Ignore the confirm.
*/
WARNING_OUT(("Domain::TokenReleaseConfirm: initiator not valid"));
}
}
}
else
{
/*
* This confirm was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::TokenReleaseConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void TokenTestRequest ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by a user attachment that wishes
* to test a token. It flows upward to the Top Provider, who attempts
* to satisfy the request.
*/
Void Domain::TokenTestRequest (
CAttachment *pOrigAtt,
UserID uidInitiator,
TokenID token_id)
{
PToken token;
/*
* Make sure the requesting user really exists in the sub-tree from which
* this request originated.
*/
if (ValidateUserID(uidInitiator, pOrigAtt))
{
/*
* If the token already exists in the token list, then let the Token
* object deal with the request.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
TRACE_OUT(("Domain::TokenTestRequest: sending test request to token object"));
token->TokenTestRequest(pOrigAtt, uidInitiator, token_id);
}
else
{
/*
* Check to see if this is the Top Provider. If it is, then the
* request can be processed locally. Otherwise, forward the
* request upward.
*/
if (IsTopProvider())
{
/*
* If the token is not in the list, send a confirm back to
* the initiating user telling it that the token is not in use.
*/
ERROR_OUT(("Domain::TokenTestRequest: no such token - available"));
pOrigAtt->TokenTestConfirm(uidInitiator, token_id, TOKEN_NOT_IN_USE);
}
else
{
/*
* This is not the Top Provider. Forward the request upward.
*/
TRACE_OUT(("Domain::TokenTestRequest: forwarding test request to Top Provider"));
m_pConnToTopProvider->TokenTestRequest(uidInitiator, token_id);
}
}
}
else
{
/*
* There is no such user in the sub-tree from which this request
* originated. Ignore the request.
*/
ERROR_OUT(("Domain::TokenTestRequest: invalid originator=0x%p", pOrigAtt));
}
}
/*
* Void TokenTestConfirm ()
*
* Public
*
* Functional Description:
* This MCS command is initially sent by the Top Provider upon receipt of
* a test request. It is sent back to the initiating user, containing
* the result of the request.
*/
Void Domain::TokenTestConfirm (
PConnection pOrigConn,
UserID uidInitiator,
TokenID token_id,
TokenStatus token_status)
{
PToken token;
/*
* Verify that the confirm came from the Top Provider.
*/
if (pOrigConn == m_pConnToTopProvider)
{
/*
* See if the token already exists in the local information base. If
* so, let it handle this.
*/
if (NULL != (token = m_TokenList2.Find(token_id)))
{
TRACE_OUT(("Domain::TokenTestConfirm: sending test confirm to token object"));
token->TokenTestConfirm(uidInitiator, token_id, token_status);
}
else
{
/*
* Make sure that the requesting user is still somewhere in the
* sub-tree of this provider.
*/
if (ValidateUserID (uidInitiator, NULL) )
{
PChannel channel;
/*
* Determine which attachment leads to the requesting user.
* Then forward the confirm in that direction.
*/
TRACE_OUT(("Domain::TokenTestConfirm: forwarding test confirm"));
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
{
CAttachment *pAtt = channel->GetAttachment();
if (pAtt)
{
pAtt->TokenTestConfirm(uidInitiator, token_id, token_status);
}
else
{
ERROR_OUT(("Domain::TokenTestConfirm: cannot get attachment"));
}
}
else
{
ERROR_OUT(("Domain::TokenTestConfirm: cannot find channel"));
}
}
else
{
/*
* The named initiator does not exist in the sub-tree of this
* provider. Ignore the confirm.
*/
ERROR_OUT(("Domain::TokenTestConfirm: initiator not valid uidInitiator=%u", (UINT) uidInitiator));
}
}
}
else
{
/*
* This confirm was received from someone besides the Top Provider.
* Ignore the indication.
*/
ERROR_OUT(("Domain::TokenTestConfirm: invalid originator=0x%p", pOrigConn));
}
}
/*
* Void LockDomainParameters ()
*
* Private
*
* Functional Description:
* This routine is used to initialize the values of the domain parameters
* instance variable.
*
* Formal Parameters:
* domain_parameters
* This is a pointer to the domain parameters structure from which the
* values are to be obtained. If it is set to NULL, then put a default
* set of parameters into the instance variable.
* parameters_locked
* This parameter indicates whether or not these parameters have been
* locked into the domain by acceptance of the first connection.
*
* Return Value:
* None.
*
* Side Effects:
* None.
*/
Void Domain::LockDomainParameters (
PDomainParameters domain_parameters,
BOOL parameters_locked)
{
/*
* If the structure pointer is valid, then copy the structure into the
* internal instance variable.
*/
if (domain_parameters != NULL)
Domain_Parameters = *domain_parameters;
else
{
/*
* Set default values for all domain parameters.
*/
Domain_Parameters.max_channel_ids = DEFAULT_MAXIMUM_CHANNELS;
Domain_Parameters.max_user_ids = DEFAULT_MAXIMUM_USERS;
Domain_Parameters.max_token_ids = DEFAULT_MAXIMUM_TOKENS;
Domain_Parameters.number_priorities = DEFAULT_NUMBER_OF_PRIORITIES;
Domain_Parameters.min_throughput = DEFAULT_MINIMUM_THROUGHPUT;
Domain_Parameters.max_height = DEFAULT_MAXIMUM_DOMAIN_HEIGHT;
Domain_Parameters.max_mcspdu_size = DEFAULT_MAXIMUM_PDU_SIZE;
Domain_Parameters.protocol_version = DEFAULT_PROTOCOL_VERSION;
if (g_fWinsockDisabled)
{
Domain_Parameters.number_priorities = DEFAULT_NUM_PLUGXPRT_PRIORITIES;
}
}
/*
* Indicate whether or not these parameters are locked.
*/
Domain_Parameters_Locked = parameters_locked;
}
/*
* ChannelID AllocateDynamicChannel ()
*
* Private
*
* Functional Description:
* This member function is used to allocate an unused channel ID in the
* dynamic range (1001 - 65535). It uses a random number generator to
* perform this task.
*
* Formal Parameters:
* None.
*
* Return Value:
* A channel ID in the dynamic range that is guaranteed to be unused.
*
* Side Effects:
* None.
*/
ChannelID Domain::AllocateDynamicChannel ()
{
ChannelID channel_id;
/*
* Stay in this loop until a unused channel ID is found. Note that this
* loop make sthe assumption that there will be at least one unused ID
* in there somewhere.
*/
while (TRUE)
{
/*
* Get a random number in the dynamic channel range.
*/
channel_id = (ChannelID) Random_Channel_Generator.GetRandomChannel ();
/*
* If it is not is use, then break out of the loop and return the
* channel ID.
*/
if (! m_ChannelList2.Find(channel_id))
break;
}
return (channel_id);
}
/*
* BOOL ValidateUserID ()
*
* Private
*
* Functional Description:
* This function is used to validate a user ID. It can be used in one of
* two ways. If the passed in attachment is NULL, then this routine will
* check to see if the ID corresponds to a user ID anywhere in the sub-tree
* of this provider. If the passed in attachment is not NULL, then this
* routine checks to see if the ID is valid user ID associated with that
* particular attachment.
*
* Formal Parameters:
* user_id
* This is the ID to be checked for validity.
* attachment
* This is the attachment that is presumably associated with the user
* ID. If NULL, we are checking for validity irrespective of
* attachment.
*
* Return Value:
* This routine will return TRUE if the user ID valid. FALSE otherwise.
*
* Side Effects:
* None.
*/
BOOL Domain::ValidateUserID (
UserID user_id,
CAttachment *pAtt)
{
PChannel channel;
/*
* Is the user ID even contained in the channel list.
*/
if (NULL != (channel = m_ChannelList2.Find(user_id)))
{
/*
* It is in the channel list. Now check to see if it corresponds to
* a user ID channel.
*/
if (channel->GetChannelType () == USER_CHANNEL)
{
/*
* Check to make sure that the real user attachment matches the
* passed in one (unless the passed in one is NULL, in which
* case it automatically matches).
*/
if ((pAtt == NULL) || (pAtt == channel->GetAttachment()))
return TRUE;
}
}
return (FALSE);
}
/*
* Void PurgeDomain ()
*
* Private
*
* Functional Description:
* This function is used to purge the entire domain. This can happen for
* two reasons. Either the Top Provider is lost, or the local user has
* asked for the domain to be deleted. Either way, this function breaks
* all attachments, and frees up all resources in use by the domain.
*
* Formal Parameters:
* None.
*
* Return Value:
* None.
*
* Side Effects:
* The domain information base is returned to its initial state.
*/
Void Domain::PurgeDomain (
Reason reason)
{
CAttachment *pAtt;
PChannel channel;
CUidList user_list;
UserID uid;
/*
* If there is a Top Provider, send a disconnect to it.
*/
if (m_pConnToTopProvider != NULL)
{
TRACE_OUT(("Domain::PurgeDomain: disconnecting top provider"));
m_pConnToTopProvider->DisconnectProviderUltimatum (reason);
m_pConnToTopProvider = NULL;
}
/*
* Send disconnects to all downward attachments. Then clear out the
* attachment list.
*/
TRACE_OUT(("Domain::PurgeDomain: disconnecting all downward attachments"));
while (NULL != (pAtt = m_AttachmentList.Get()))
{
pAtt->DisconnectProviderUltimatum(reason);
/*
* If there are any pending attach user requests on the attachment
* that was just broken, delete them. Note that this is a loop
* because there can be more than one.
*/
while (m_AttachUserQueue.Remove(pAtt));
}
/*
* Send a disconnect to all attachments that represent attach user requests
* in process. Then clear the queue out.
*/
while (NULL != (pAtt = m_AttachUserQueue.Get()))
{
pAtt->DisconnectProviderUltimatum(reason);
}
/*
* Clear the merge queue. The actual attachments have already been broken
* above.
*/
m_MergeQueue.Clear();
/*
* We cannot just delete all channels and tokens, because doing so would
* cause them to issue various indications to attachments that are no
* longer valid. To get around this, we must delete all attachments (which
* was done above) and all user objects from the channel list, and then
* reclaim unowned resources. This will cause all static, assigned, and
* private channels, as well as tokens, to delete themselves.
*/
m_ChannelList2.Reset();
while (NULL != (channel = m_ChannelList2.Iterate(&uid)))
{
if (channel->GetChannelType () == USER_CHANNEL)
user_list.Append(uid);
}
/*
* Delete all users from the channel list. Since there are no valid users
* in the domain, all resources that are tied to users will be reclaimed
* below.
*/
user_list.Reset();
while (NULL != (uid = user_list.Iterate()))
{
DeleteChannel((ChannelID) uid);
}
/*
* Reclaim unowned resources. Since all resources (channels and tokens)
* are tied to the existence of either attachments or users, this call
* will result in all channels and tokens being cleanly deleted (since
* there aren't any attachments or users).
*/
ReclaimResources ();
/*
* Reset the state to all initial values.
*/
Merge_State = MERGE_INACTIVE;
Outstanding_Merge_Requests = 0;
Number_Of_Users = 0;
Number_Of_Channels = 0;
Number_Of_Tokens = 0;
m_nDomainHeight = 0;
m_DomainHeightList2.Clear();
LockDomainParameters (NULL, FALSE);
}
/*
* Void DeleteAttachment ()
*
* Private
*
* Functional Description:
* This function is used to free up all resources that are "bound" to
* particular attachment. It also deletes the downward attachment.
*
* Formal Parameters:
* attachment
* This is the attachment to be deleted.
* reason
* This is the reason for the deletion. This is merely passed on in
* any MCS commands that are sent as a result of this deletion.
*
* Return Value:
* None.
*
* Side Effects:
* Resources in the domain information base are freed up.
*/
Void Domain::DeleteAttachment (
CAttachment *pAtt,
Reason reason)
{
ChannelID chid;
PChannel channel;
CUidList user_deletion_list;
CChannelIDList channel_deletion_list;
CChannelIDList channel_leave_list;
/*
* Check to make sure the attachment is real before proceeding.
*/
if (m_AttachmentList.Remove(pAtt))
{
/*
* Remove the attachment from the downward attachment list.
*/
/*
* Iterate through the channel list building two lists, as follows:
*
* 1. A list of users who lie in the direction of the lost attachment.
* These users must be deleted from the information base, and
* their detachment reported appropriately.
* 2. A list of channels that must be deleted as a result of the lost
* attachment. This list is created by sending a channel leave
* request to all channels, and then checking to see if they are
* still valid. All static and assigned channels that only had
* that attachment joined will be deleted as a result of this.
* This also results in the attachment being removed from all
* channel attachment lists, avoiding the possibility of sending
* data to an invalid attachment.
*/
m_ChannelList2.Reset();
while (NULL != (channel = m_ChannelList2.Iterate(&chid)))
{
/*
* Check to see if this is a user ID channel whose user lies on the
* other side of the lost attachment. If so, add the channel to
* the deletion list.
*/
if (channel->GetChannelType () == USER_CHANNEL)
{
if (channel->GetAttachment() == pAtt)
{
user_deletion_list.Append(chid);
continue;
}
}
/*
* Issue the leave request to the channel. Then check to see if it
* is still valid. If not, then add it to the deletion list.
*/
channel_leave_list.Clear();
channel_leave_list.Append(chid);
channel->ChannelLeaveRequest(pAtt, &channel_leave_list);
if (channel->IsValid () == FALSE)
channel_deletion_list.Append(chid);
}
/*
* Iterate through the channel list, deleting the channels it
* contains.
*/
channel_deletion_list.Reset();
while (NULL != (chid = channel_deletion_list.Iterate()))
{
DeleteChannel(chid);
}
/*
* If there are any users to be deleted, simulate a DetachUserRequest
* with the list of users to be deleted.
*/
if (user_deletion_list.IsEmpty() == FALSE)
DetachUserRequest(pAtt, reason, &user_deletion_list);
/*
* Check to see if the deleted attachment is represented in the
* domain height list. If it is, then this loss could result in a
* change in the overall domain height.
*/
if (m_DomainHeightList2.Remove((PConnection) pAtt))
{
/*
* The attachment is in the list. Remove it from the list, and
* call the subroutine that determines whether an overall height
* change has occurred that may require further activity.
*/
CalculateDomainHeight ();
}
}
else
{
/*
* The named attachment isn't even in the attachment list.
*/
ERROR_OUT(("Domain::DeleteAttachment: unknown attachment=0x%p", pAtt));
}
}
/*
* Void DeleteUser ()
*
* Private
*
* Functional Description:
* This routine deletes a user from the information base. This is fairly
* complex task because there are a lot of dependencies on users within
* the MCS protocol. If the user being deleted is locall attached, then
* the attachment must be severed. Also, any resources that are being
* held by the user must be reclaimed. And finally, the user channel
* object that represents the user must be deleted from the local channel
* list.
*
* Formal Parameters:
* user_id
* This is the ID of the user being deleted.
*
* Return Value:
* None.
*
* Side Effects:
* None.
*/
Void Domain::DeleteUser (
UserID user_id)
{
CAttachment *pAtt;
ChannelID chid;
PChannel channel;
CChannelIDList deletion_list;
CChannelIDList channel_leave_list;
/*
* Make sure this is a valid user in the sub-tree of this provider before
* proceeding.
*/
if (ValidateUserID (user_id, NULL) )
{
/*
* Determine which attachment leads to the user in question.
*/
if (NULL != (channel = m_ChannelList2.Find(user_id)))
{
pAtt = channel->GetAttachment();
/*
* Delete the user channel now that it is no longer necessary.
*/
DeleteChannel (user_id);
/*
* Check to see if the user's attachment is still valid. It is
* possible that the user is being deleted as a result of losing the
* attachment that leads to it.
*/
if (m_AttachmentList.Find(pAtt) && pAtt->IsUserAttachment())
{
/*
* If this user was locally attached, then it is necessary to
* remove it from the attachment list, as well as making sure that
* no other channel objects attempt to reference it.
*/
/*
* Remove the attachment from the downward attachment list.
*/
TRACE_OUT(("Domain::DeleteUser: deleting local attachment"));
m_AttachmentList.Remove(pAtt);
((PUser) pAtt)->Release();
/*
* Iterate through the channel list issuing leave requests to
* each channel. This prevents a Channel object from trying to
* send data on an attachment that is no longer valid. This
* loop also builds a list of Channel objects that should be
* deleted as a result of this detachment.
*/
m_ChannelList2.Reset();
while (NULL != (channel = m_ChannelList2.Iterate(&chid)))
{
/*
* Issue the leave request to the channel. Then check to
* see if it is still valid. If not, then add it to the
* deletion list.
*/
channel_leave_list.Clear();
channel_leave_list.Append(chid);
channel->ChannelLeaveRequest(pAtt, &channel_leave_list);
if (channel->IsValid () == FALSE)
deletion_list.Append(chid);
}
/*
* Iterator through the deletion list, deleting the channels it
* contains.
*/
deletion_list.Reset();
while (NULL != (chid = deletion_list.Iterate()))
{
DeleteChannel(chid);
}
}
/*
* Reclaim all resources that may have been freed as a result of the
* deleted user.
*/
ReclaimResources ();
}
else
{
ERROR_OUT(("Domain::DeleteUser: cannot find channel"));
}
}
else
{
/*
* The specified user ID is not valid.
*/
ERROR_OUT(("Domain::DeleteUser: unknown user ID"));
}
}
/*
* Void DeleteChannel ()
*
* Private
*
* Functional Description:
* This function deleted a channel from the channel list. It also adjusts
* the appropriate channel counter (according to type), and reports the
* deletion.
*
* Formal Parameters:
* channel_id
* This is the ID of the channel to be deleted.
*
* Return Value:
* None.
*
* Side Effects:
* None.
*/
Void Domain::DeleteChannel (
ChannelID channel_id)
{
PChannel channel;
/*
* Make sure the channel being deleted is real before proceeding.
*/
if (NULL != (channel = m_ChannelList2.Remove(channel_id)))
{
/*
* Report the type of channel being deleted, and decrement the
* appropriate counter.
*/
Number_Of_Channels--;
switch (channel->GetChannelType ())
{
case STATIC_CHANNEL:
TRACE_OUT (("Domain::DeleteChannel: "
"deleting static channel ID = %04X", channel_id));
break;
case ASSIGNED_CHANNEL:
TRACE_OUT (("Domain::DeleteChannel: "
"deleting assigned channel ID = %04X", channel_id));
break;
case USER_CHANNEL:
TRACE_OUT (("Domain::DeleteChannel: "
"deleting user channel ID = %04X", channel_id));
Number_Of_Users--;
break;
case PRIVATE_CHANNEL:
TRACE_OUT (("Domain::DeleteChannel: "
"deleting private channel ID = %04X", channel_id));
break;
default:
ERROR_OUT (("Domain::DeleteChannel: "
"ERROR - deleting unknown channel ID = %04X",
channel_id));
Number_Of_Channels++;
break;
}
/*
* Delete the channel object.
*/
delete channel;
}
else
{
ERROR_OUT(("Domain::DeleteChannel: unknown channel ID"));
}
}
/*
* Void DeleteToken ()
*
* Private
*
* Functional Description:
* This function deletes a token from the token list. It also adjusts
* the token counter.
*
* Formal Parameters:
* token_id
* This is the ID of the token to be deleted.
*
* Return Value:
* None.
*
* Side Effects:
* None.
*/
Void Domain::DeleteToken (
TokenID token_id)
{
PToken token;
/*
* Check to make sure that the token being deleted is real before
* proceeding.
*/
if (NULL != (token = m_TokenList2.Remove(token_id)))
{
/*
* Remove the token from the token list and delete it.
*/
TRACE_OUT(("Domain::DeleteToken: deleting token ID = %04X", (UINT) token_id));
delete token;
/*
* Decrement the token counter.
*/
Number_Of_Tokens--;
}
else
{
ERROR_OUT(("Domain::DeleteToken: unknown token ID"));
}
}
/*
* Void ReclaimResources ()
*
* Private
*
* Functional Description:
* This function walks through both the channel and token lists, removing
* all objects that are no longer valid. This function just queries each
* channel and token to see if it is still valid. This allows for the
* reclamation of resources when a user is deleted.
*
* Formal Parameters:
* None.
*
* Return Value:
* None.
*
* Side Effects:
* None.
*/
Void Domain::ReclaimResources ()
{
ChannelID chid;
PChannel channel;
CChannelIDList channel_deletion_list;
TokenID tid;
PToken token;
CTokenIDList token_deletion_list;
/*
* Iterate through the channel list, asking each channel if it is still
* valid. Any that are not will be deleted by the next loop.
*/
m_ChannelList2.Reset();
while (NULL != (channel = m_ChannelList2.Iterate(&chid)))
{
/*
* Check to see if the channel is still valid. If not, add it to the
* deletion list.
*/
if (channel->IsValid () == FALSE)
channel_deletion_list.Append(chid);
}
/*
* Delete all channels in the deletion list.
*/
channel_deletion_list.Reset();
while (NULL != (chid = channel_deletion_list.Iterate()))
{
DeleteChannel(chid);
}
/*
* Iterate through the token list, asking each token if it is still
* valid. Any that are not will be deleted by the next loop.
*/
m_TokenList2.Reset();
while (NULL != (token = m_TokenList2.Iterate(&tid)))
{
/*
* Check to see if the token is still valid. If the grabber or
* inhibitor was the only owner of the token, then it will be freed
* here.
*/
if (token->IsValid () == FALSE)
token_deletion_list.Append(tid);
}
/*
* Delete all tokens in the deletion list.
*/
while (NULL != (tid = token_deletion_list.Get()))
{
DeleteToken(tid);
}
}
/*
* Void MergeInformationBase ()
*
* Private
*
* Functional Description:
* This function is essentially a state machine for the domain merger
* process. Domain merging is currently implemented to only try and
* merge one type of resource at a time. Each time this routine is
* called, the next type of resource is merged. After all resources have
* been merged, this provider ceases to be a Top Provider, and the merge
* state is returned to inactive.
*
* Formal Parameters:
* None.
*
* Return Value:
* None.
*
* Side Effects:
* Contents of the domain information are merged upward to the Top
* Provider of the upper domain.
*/
Void Domain::MergeInformationBase ()
{
MergeState merge_state;
Channel_Type channel_type;
PChannel channel;
PToken token;
/*
* This call is not valid unless there is a Top Provider to merge the
* information upward to.
*/
if (m_pConnToTopProvider != NULL)
{
/*
* As part of the transition to the next merge state, set the number
* of outstanding merge requests to 0.
*/
Outstanding_Merge_Requests = 0;
merge_state = Merge_State;
while (Outstanding_Merge_Requests == 0)
{
/*
* Each case of this switch statement sets two variables. The
* first is the merge state. This is bumped to the next state
* in the sequence. The second is the channel type. This is
* used for controlling which type of channel is being merged
* upward for this state.
*/
switch (merge_state)
{
case MERGE_INACTIVE:
TRACE_OUT(("Domain::MergeInformationBase: merging User IDs"));
merge_state = MERGE_USER_IDS;
channel_type = USER_CHANNEL;
break;
case MERGE_USER_IDS:
TRACE_OUT(("Domain::MergeInformationBase: merging Static Channels"));
merge_state = MERGE_STATIC_CHANNELS;
channel_type = STATIC_CHANNEL;
break;
case MERGE_STATIC_CHANNELS:
TRACE_OUT(("Domain::MergeInformationBase: merging Assigned Channels"));
merge_state = MERGE_ASSIGNED_CHANNELS;
channel_type = ASSIGNED_CHANNEL;
break;
case MERGE_ASSIGNED_CHANNELS:
TRACE_OUT(("Domain::MergeInformationBase: merging Private Channels"));
merge_state = MERGE_PRIVATE_CHANNELS;
channel_type = PRIVATE_CHANNEL;
break;
case MERGE_PRIVATE_CHANNELS:
TRACE_OUT(("Domain::MergeInformationBase: merging Tokens"));
merge_state = MERGE_TOKENS;
break;
case MERGE_TOKENS:
TRACE_OUT(("Domain::MergeInformationBase: domain merger complete"));
merge_state = MERGE_COMPLETE;
break;
default:
ERROR_OUT(("Domain::MergeInformationBase: invalid merge state"));
break;
}
/*
* If the merge is now complete, then this provider must cease
* to be a Top Provider.
*/
if (merge_state == MERGE_COMPLETE)
{
/*
* Reset the merge state, and break out of this loop.
*/
merge_state = MERGE_INACTIVE;
break;
}
/*
* Check to see if we are to merge tokens on this pass.
*/
if (merge_state == MERGE_TOKENS)
{
/*
* Iterate through the token list, sending merge requests to
* each Token object. Pass in the identity of the pending
* Top Provider, so that the Token object knows where to send
* the MergeTokensRequest. Increment the number of
* outstanding merge requests.
*/
m_TokenList2.Reset();
while (NULL != (token = m_TokenList2.Iterate()))
{
token->IssueMergeRequest ();
Outstanding_Merge_Requests++;
}
}
else
{
/*
* This must be a merge state for channels. Iterate through
* the channel list, sending a merge request to each Channel
* object whose type matches that specified by the merge
* state that we are. Increment the outstanding merge
* request counter each time one is sent.
*/
m_ChannelList2.Reset();
while (NULL != (channel = m_ChannelList2.Iterate()))
{
if (channel->GetChannelType () == channel_type)
{
channel->IssueMergeRequest ();
Outstanding_Merge_Requests++;
}
}
}
}
SetMergeState (merge_state);
}
else
{
/*
* This routine has been called when the domain is not in the
* appropriate state.
*/
ERROR_OUT(("Domain::MergeInformationBase: unable to merge at this time"));
}
}
/*
* Void SetMergeState ()
*
* Private
*
* Functional Description:
* This function sets the merge state to the passed in value. It also
* detects the transition between MERGE_INACTIVE and any other state.
* This transition causes the domain to issue MergeDomainIndication to
* all downward attachments.
*
* Formal Parameters:
* merge_state
* This is the merge state that we are moving to.
*
* Return Value:
* None.
*
* Side Effects:
* None.
*/
Void Domain::SetMergeState (
MergeState merge_state)
{
CAttachment *pAtt;
/*
* Don't do anything unless the merge state is actually changing.
*/
if (Merge_State != merge_state)
{
/*
* If the old state is inactive, then that means that we are just
* beginning a domain merge operation. If this is the case, then
* iterate through the downward attachment list, telling all
* attachments about the domain merge.
*
* Note that a side effect of this call is that all MCS commands
* are shut off from the attachments that receive it. This allows the
* domain information base to remain stable during a merge operation.
*/
if (Merge_State == MERGE_INACTIVE)
{
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->MergeDomainIndication(MERGE_DOMAIN_IN_PROGRESS);
}
}
/*
* Set the merge state.
*/
Merge_State = merge_state;
/*
* If the new state is inactive, then that means that we have just
* completed a domain merge operation. If this is the case, then
* iterate through the downward attachment list, telling all
* attachments about the completion of the merge.
*
* Note that a side effect of this call is to re-enable MCS commands
* from the attachments that receive it.
*/
if (Merge_State == MERGE_INACTIVE)
{
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->MergeDomainIndication(MERGE_DOMAIN_COMPLETE);
}
}
}
}
/*
* Void AddChannel ()
*
* Private
*
* Functional Description:
* This function is used to add a channel to the channel list during a
* merge channel operation. This type of channel addition works a little
* differently, since we do not want to send confirms to the users, but
* rather, to former Top Provider of the lower domain.
*
* Formal Parameters:
* attachment
* This is the initial attachment that the channel is to have joined
* to it.
* merge_channel
* This is a pointer to a channel attributes structure containing the
* attributes of the channel to be added.
* merge_channel_list
* This is a list of channel attribute structures for those channels
* that were successfully merged into the domain information base. It
* will be used to issue the merge channels confirm downward.
* purge_channel_list
* This is a list of channel IDs for those channels that were not
* successfully merged into the domain information base. It will be
* used to issue the merge channels confirm downward.
*
* Return Value:
* None.
*
* Side Effects:
* None.
*/
Void Domain::AddChannel (
PConnection pConn,
PChannelAttributes merge_channel,
CChannelAttributesList *merge_channel_list,
CChannelIDList *purge_channel_list)
{
Channel_Type channel_type;
ChannelID channel_id=0;
BOOL joined;
ChannelID channel_manager=0;
CUidList *admitted_list;
PChannel channel=NULL;
CUidList detach_user_list;
CChannelIDList channel_leave_list;
/*
* Determine what has to be done according to the channel type.
*/
channel_type = merge_channel->channel_type;
switch (channel_type)
{
case STATIC_CHANNEL:
/*
* Get the channel ID from the attributes structure.
*/
channel_id = merge_channel->u.static_channel_attributes.channel_id;
/*
* If this is the Top Provider, check to see if current
* domain parameters will permit the merge.
*/
if ((m_pConnToTopProvider == NULL) &&
(Number_Of_Channels >= Domain_Parameters.max_channel_ids))
{
ERROR_OUT(("Domain::AddChannel: too many channels"));
channel = NULL;
break;
}
/*
* Attempt to create a new Channel object.
*/
TRACE_OUT(("Domain::AddChannel: adding new channel ID = %04X", (UINT) channel_id));
DBG_SAVE_FILE_LINE
channel = new Channel(channel_id, this, m_pConnToTopProvider, &m_ChannelList2,
&m_AttachmentList, pConn);
/*
* Increment the number of channels if everything went okay.
*/
if (channel != NULL)
Number_Of_Channels++;
break;
case USER_CHANNEL:
/*
* Get the channel ID from the attributes structure.
*/
joined = merge_channel->u.user_channel_attributes.joined;
channel_id = merge_channel->u.user_channel_attributes.user_id;
/*
* If this is the Top Provider, check to see if current
* domain parameters will permit the merge.
*/
if ((m_pConnToTopProvider == NULL) &&
((Number_Of_Users >= Domain_Parameters.max_user_ids) ||
(Number_Of_Channels >= Domain_Parameters.max_channel_ids)))
{
ERROR_OUT(("Domain::AddChannel: too many users"));
channel = NULL;
break;
}
/*
* Attempt to create a new UserChannel object to represent the
* merged user ID.
*/
TRACE_OUT(("Domain::AddChannel: adding new user ID = %04X", (UINT) channel_id));
DBG_SAVE_FILE_LINE
channel = new UserChannel(channel_id, pConn, this, m_pConnToTopProvider,
&m_ChannelList2, &m_AttachmentList, joined ? pConn : NULL);
/*
* Increment the number of users if everything went okay.
*/
if (channel != NULL)
{
Number_Of_Users++;
Number_Of_Channels++;
}
break;
case PRIVATE_CHANNEL:
/*
* Get the channel ID and the channel manager ID from the
* attributes structure.
*/
joined = merge_channel->u.private_channel_attributes.joined;
channel_id = merge_channel->u.private_channel_attributes.channel_id;
channel_manager = merge_channel->
u.private_channel_attributes.channel_manager;
admitted_list = merge_channel->
u.private_channel_attributes.admitted_list;
/*
* If this is the Top Provider, check to see if current
* domain parameters will permit the merge.
*/
if ((m_pConnToTopProvider == NULL) &&
(Number_Of_Channels >= Domain_Parameters.max_channel_ids))
{
ERROR_OUT(("Domain::AddChannel: too many channels"));
channel = NULL;
break;
}
/*
* Attempt to create a new PrivateChannel object.
*/
TRACE_OUT(("Domain::AddChannel: adding new private channel ID = %04X", (UINT) channel_id));
DBG_SAVE_FILE_LINE
channel = new PrivateChannel(channel_id, channel_manager, this, m_pConnToTopProvider,
&m_ChannelList2, &m_AttachmentList, admitted_list,
joined ? pConn : NULL);
/*
* Increment the number of channels if everything went okay.
*/
if (channel != NULL)
Number_Of_Channels++;
break;
case ASSIGNED_CHANNEL:
/*
* Get the channel ID from the attributes structure.
*/
channel_id = merge_channel->
u.assigned_channel_attributes.channel_id;
/*
* If this is the Top Provider, check to see if current
* domain parameters will permit the merge.
*/
if ((m_pConnToTopProvider == NULL) &&
(Number_Of_Channels >= Domain_Parameters.max_channel_ids))
{
ERROR_OUT(("Domain::AddChannel: too many channels"));
channel = NULL;
break;
}
/*
* Attempt to create a new Channel object.
*/
TRACE_OUT(("Domain::AddChannel: adding new channel ID = %04X", (UINT) channel_id));
DBG_SAVE_FILE_LINE
channel = new Channel(channel_id, this, m_pConnToTopProvider, &m_ChannelList2,
&m_AttachmentList, pConn);
/*
* Increment the number of channels if everything went okay.
*/
if (channel != NULL)
Number_Of_Channels++;
break;
}
if (channel != NULL)
{
/*
* The channel was successfully created. Add it to the channel list
* and add the channel structure to the merge channel list, which is
* used to issue the merge channels confirm downward.
*/
m_ChannelList2.Insert(channel_id, channel);
merge_channel_list->Append(merge_channel);
}
else
{
/*
* The channel merge operation has failed. We need to tell whoever
* is interested in this situation.
*/
WARNING_OUT(("Domain::AddChannel: channel merger failed"));
if (m_pConnToTopProvider != NULL)
{
/*
* If this is not the Top Provider, then the Top Provider needs
* to be told about the problem. If this is a user channel, then
* issue a detach user request. If it is a normal channel, issue
* a channel leave request. If it is a private channel, issue a
* channel disband request.
*/
switch (channel_type)
{
case STATIC_CHANNEL:
case ASSIGNED_CHANNEL:
TRACE_OUT(("Domain::AddChannel: sending ChannelLeaveRequest to Top Provider"));
channel_leave_list.Append(channel_id);
m_pConnToTopProvider->ChannelLeaveRequest(&channel_leave_list);
break;
case USER_CHANNEL:
TRACE_OUT(("Domain::AddChannel: sending DetachUserRequest to Top Provider"));
detach_user_list.Append(channel_id);
m_pConnToTopProvider->DetachUserRequest(REASON_PROVIDER_INITIATED, &detach_user_list);
break;
case PRIVATE_CHANNEL:
TRACE_OUT(("Domain::AddChannel: sending ChannelDisbandRequest to Top Provider"));
m_pConnToTopProvider->ChannelDisbandRequest(channel_manager, channel_id);
break;
}
}
/*
* Since the merge has failed, we need to put the channel ID into the
* purge channel list (which is used to issue the merge channels
* confirm downward).
*/
purge_channel_list->Append(channel_id);
}
}
/*
* Void AddToken ()
*
* Private
*
* Functional Description:
* This function is used to add a token to the token list during a
* merge token operation. This type of token addition works a little
* differently, since we do not want to send confirms to the owners of
* the token, but rather, to the former Top Provider of the lower domain.
*
* Formal Parameters:
* merge_token
* This is a pointer to a token attributes structure containing the
* attributes of the token to be added.
* merge_token_list
* This is a list of token attribute structures for those tokens
* that were successfully merged into the domain information base. It
* will be used to issue the merge tokens confirm downward.
* purge_token_list
* This is a list of token IDs for those tokens that were not
* successfully merged into the domain information base. It will be
* used to issue the merge tokens confirm downward.
*
* Return Value:
* None.
*
* Side Effects:
* None.
*/
Void Domain::AddToken (
PTokenAttributes merge_token,
CTokenAttributesList *merge_token_list,
CTokenIDList *purge_token_list)
{
TokenState token_state;
TokenID token_id;
UserID grabber;
CUidList *inhibitor_list;
UserID recipient;
PToken token;
UserID uid;
/*
* Determine what state the token to be merged is in. Then get the
* required information for each particular state.
*/
token_state = merge_token->token_state;
switch (token_state)
{
case TOKEN_GRABBED:
token_id = merge_token->u.grabbed_token_attributes.token_id;
grabber = merge_token->u.grabbed_token_attributes.grabber;
inhibitor_list = NULL;
recipient = 0;
break;
case TOKEN_INHIBITED:
token_id = merge_token->u.inhibited_token_attributes.token_id;
grabber = 0;
inhibitor_list = merge_token->
u.inhibited_token_attributes.inhibitors;
recipient = 0;
break;
case TOKEN_GIVING:
token_id = merge_token->u.giving_token_attributes.token_id;
grabber = merge_token->u.giving_token_attributes.grabber;
inhibitor_list = NULL;
recipient = merge_token->u.giving_token_attributes.recipient;
break;
case TOKEN_GIVEN:
token_id = merge_token->u.given_token_attributes.token_id;
grabber = 0;
inhibitor_list = NULL;
recipient = merge_token->u.given_token_attributes.recipient;
break;
}
/*
* Check to see if it is okay to add this token. If we are the top
* provider, and adding this token would cause us to exceed the arbitrated
* limit on tokens, then we must fail the request.
*/
if ((m_pConnToTopProvider != NULL) ||
(Number_Of_Tokens < Domain_Parameters.max_token_ids))
{
/*
* Create a new token with all merged values as determined above.
*/
DBG_SAVE_FILE_LINE
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2,
&m_AttachmentList, token_state, grabber, inhibitor_list, recipient);
if (token != NULL)
{
/*
* If the creation was successful, add the token to the list and
* add the token attributes structure to the merge token list.
*/
TRACE_OUT(("Domain::AddToken: add new token ID = %04X", (UINT) token_id));
m_TokenList2.Append(token_id, token);
Number_Of_Tokens++;
merge_token_list->Append(merge_token);
}
else
{
/*
* The token allocation has failed. It is therefore necessary to
* perform some cleanup operations.
*/
WARNING_OUT (("Domain::AddToken: token allocation failed"));
/*
* Check to see if this is the top provider. If not, then it
* is necessary to issue appropriate requests upward to free the
* token from the information bases above.
*/
if (m_pConnToTopProvider != NULL)
{
/*
* Determine which state the token is in. This affects how
* the cleanup needs to work.
*/
switch (token_state)
{
case TOKEN_GRABBED:
/*
* If the token is grabbed, then issue a release to
* free it above.
*/
m_pConnToTopProvider->TokenReleaseRequest(grabber, token_id);
break;
case TOKEN_INHIBITED:
{
/*
* Iterate through the inhibitor list, issuing a
* release request for each user contained therein.
* This will result in the token being freed at all
* upward providers.
*/
inhibitor_list->Reset();
while (NULL != (uid = inhibitor_list->Iterate()))
{
m_pConnToTopProvider->TokenReleaseRequest(uid, token_id);
}
}
break;
case TOKEN_GIVING:
/*
* If the token is being given from one user to
* another, issue a release on behalf of the current
* owner, and a rejected give response on behalf
* of the recipient.
*
* WARNING:
* This will cause the current owner to receive a
* release confirm with no outstanding request.
*/
m_pConnToTopProvider->TokenReleaseRequest(grabber, token_id);
m_pConnToTopProvider->TokenGiveResponse(RESULT_USER_REJECTED,
recipient, token_id);
break;
case TOKEN_GIVEN:
/*
* Issue a rejected give response on behalf of the
* user that is being offered the token.
*/
m_pConnToTopProvider->TokenGiveResponse(RESULT_USER_REJECTED,
recipient, token_id);
break;
}
}
/*
* Add the token ID to the purge token list, which will be passed
* downward to the former top provider of the lower domain. This
* will tell that provider that the token was NOT accepted in the
* upper domain.
*/
purge_token_list->Append(token_id);
}
}
else
{
/*
* The upper domain already has the domain limit of tokens. So
* automatically reject the merge request.
*/
ERROR_OUT(("Domain::AddToken: too many tokens - rejecting merge"));
purge_token_list->Append(token_id);
}
}
/*
* Void CalculateDomainHeight ()
*
* Private
*
* Functional Description:
* This function is called whenever an event occurs that could have
* resulted in a change in the overall height of the domain. This includes
* making and breaking connections, and the reception of an erect domain
* request from a lower provider.
*
* This routine will adjust the height of the current provider, and if
* this is the top provider, will take necessary steps to insure that the
* arbitrated domain parameters are not violated.
*
* Formal Parameters:
* None.
*
* Return Value:
* None.
*
* Side Effects:
* None.
*/
Void Domain::CalculateDomainHeight ()
{
UINT domain_height;
UINT temp_domain_height;
CAttachment *pAtt;
/*
* Initialize domain height to zero. This will only be increased if there
* is at least one remote attachment below this one.
*/
domain_height = 0;
/*
* Check to see if there is anyone below this provider that would affect
* its height in the domain (this would be remote attachments that have
* issued ErectDomainRequests to tell this provider of their height).
*/
if (m_DomainHeightList2.IsEmpty() == FALSE)
{
/*
* Iterate through the domain height list to determine which downward
* attachment has the greatest height. This is the height that will be
* used to determine height of this provider.
*/
m_DomainHeightList2.Reset();
while (NULL != (temp_domain_height = m_DomainHeightList2.Iterate()))
{
if (domain_height < temp_domain_height)
domain_height = temp_domain_height;
}
/*
* The height of this provider is one greater than the height of its
* highest downward attachment.
*/
domain_height++;
}
/*
* Compare the calculated domain height with the current domain height.
* If they are the same, then no further action needs to be taken.
*/
if (domain_height != m_nDomainHeight)
{
TRACE_OUT(("Domain::CalculateDomainHeight: new domain height = %d", (UINT) domain_height));
m_nDomainHeight = domain_height;
/*
* The domain height has changed. We need to verify that the
* arbitrated domain height has not been violated.
*/
if (m_nDomainHeight > Domain_Parameters.max_height)
{
/*
* The new domain height is invalid. We must issue a plumb
* domain indication downward to enforce the arbitrated
* domain height.
*/
TRACE_OUT(("Domain::CalculateDomainHeight: issuing plumb domain indication"));
m_AttachmentList.Reset();
while (NULL != (pAtt = m_AttachmentList.Iterate()))
{
pAtt->PlumbDomainIndication(Domain_Parameters.max_height);
}
}
/*
* If this is not the Top Provider, then it is necessary to transmit an
* erect domain request upward to inform the upper domain of the
* change.
*/
if (m_pConnToTopProvider != NULL)
{
/*
* Issue an erect domain request upward to inform the upper
* domain of the change in height. Without this, the Top Provider
* would have no way of determining when the domain height is
* invalid.
*/
m_pConnToTopProvider->ErectDomainRequest(m_nDomainHeight, DEFAULT_THROUGHPUT_ENFORCEMENT_INTERVAL);
}
}
}
PUser CAttachmentList::IterateUser(void)
{
CAttachment *pAtt;
while (NULL != (pAtt = Iterate()))
{
if (pAtt->IsUserAttachment())
{
return (PUser) pAtt;
}
}
return NULL;
}
PConnection CAttachmentList::IterateConn(void)
{
CAttachment *pAtt;
while (NULL != (pAtt = Iterate()))
{
if (pAtt->IsConnAttachment())
{
return (PConnection) pAtt;
}
}
return NULL;
}