/*++

Copyright (c) 2000-2001 Microsoft Corporation

Module Name:
    initsync.c

Abstract:
    Initial Sync Controller Command Server. The initial sync controller command server
    controls the sequencing of vvjoins for a new member of the replica set.
    This command server is active only during the time the replica set is in seeding state.
    During this time the cxtions are paused and unpaused by this command server to make
    sure that only 1 vvjoin is in process at any given time. The priority on the cxtion
    is used to decide the order of vvjoins.

    Following flags are used by this command server. 

    CXTION_FLAGS_INIT_SYNC      : Location = PCXTION->Flags
    ^^^^^^^^^^^^^^^^^^^^^^
    This flag is set on all inbound connections that are added to a replica set when
    it is in the seeding state. This flag is set when the connection is initialized
    in OutLogAddNewPartner() and it is reset when the cxtion completes vvjoin in 
    InitSyncVvJoinDone. While this flag is set and the replica is in seeding state
    all decisions to join are made by the initsync command server. Once the cxtion
    completes vvjoin and this flag is cleared the cxtion is free to join at any time.
    This flag is persistent in the DB,

    CXTION_FLAGS_PAUSED         : Location = PCXTION->Flags
    ^^^^^^^^^^^^^^^^^^^
    If a connection has this flag set then it is not allowed to join with its
    inbound partner. The command server clears this flag in order. All cxtions 
    that have the CXTION_FLAGS_INIT_SYNC set start off as paused. They get unpaused
    when its their turn to vvjoin.

    CXTION_FLAGS_HUNG_INIT_SYNC : Location = PCXTION->Flags
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    This flag is set on a cxtion to indicate that there has been no progress on this
    connection for a specified time (timeout) since a NEED_JOIN was sent. If the current
    working connection gets in this state the command server may decide to unpause
    the next cxtion on list.

    CONFIG_FLAG_ONLINE          : Location PREPLICA->CnfFlags
    ^^^^^^^^^^^^^^^^^^
    Presence of this flag on the replica indicates that this member has successfully
    completed one pass through the inbound connections and is ready to go online. Until
    a member is online it will not join with its outbound partners. For a sysvol replica
    set going online also means sysvolready is set to 1. A replica set may be in
    online state but it may still be seeding. This depends on the priorities set on
    the cxtions.

    CONFIG_FLAG_SEEDING         : Location PREPLICA->CnfFlags
    ^^^^^^^^^^^^^^^^^^^
    We don't get here unless the replica set is in seeding state. A new replica 
    starts up in seeding state unless it is the primary member of the replica set.


    *******************************  CXTION OPTIONS  **********************************

    The options attribute on the "NTDS Connection" object is used to specify the
    priority for a given connection. Options is a 32 bit value. The highest bit is
    used to indicate if the schedule should be ignored while vvjoining. The next 3
    bits are used to spevify a priority from 0-7. The following two masks are used
    to get the values.

    #define FRSCONN_PRIORITY_MASK		      0x70000000
    #define NTDSCONN_OPT_IGNORE_SCHEDULE_MASK 0x80000000

    The ignore scheduel bit is not checked while the connection is in INIT_SYNC state.

    The priorities are interpretted as shown in the table below.

    Priority : Behavior
    Class

    0	     : Volatile connections have this priority.
               Highest priority class, 
               Try every cxtion in this class, 
               skip to next class on communication errors.

    1-2	     : Do not proceed to next class until done with all connections 
               of this class.

    3-4	     : Do not proceed to next class until done with at least one 
               connection of this class.

    5-7	     : Try every cxtion in this class,
               Skip to next class on communication errors.

    8        : FRSCONN_MAX_PRIORITY - A priority of '0' in the DS corresponds
               to this priority. We need to do this to maintain old 
               behavior for connections that do not have a priority set.
               Do not proceed to next class until done with at least one 
               connection from this class or any of the other classes.


     The command server forms a sorted list of the connections based on their priorities
     and then starts vvjoins on these connections one at a time. After every vvjoin
     is done it checks if the current class is satisfied based on the table above. If
     it is then cxtions from the next class are picked up and so on. When the last
     class is satisfied the member goes into Online state and is free to join with its
     outbound partners. Remaining connections will continue to vvjoin serially and when
     all are done the member goes out of seeding state.

    **********************************************************************************

Author:
    Sudarshan Chitre - 27th April 2001

Environment
    User mode winnt

--*/

#include <ntreppch.h>
#pragma  hdrstop

#undef DEBSUB
#undef DEBSUB
#define DEBSUB  "INITSYNC:"

#include <frs.h>
#include <tablefcn.h>
#include <perrepsr.h>
#include <ntdsapi.h>
//
// Struct for the Initial Sync Controller Command Server
//      Contains info about the queues and the threads
//
COMMAND_SERVER InitSyncCs;
ULONG  MaxInitSyncCsThreads;


extern PGEN_TABLE ReplicasByGuid;
//
// Replica Command server functions called from here.
//
VOID
RcsSubmitReplicaCxtionJoin(
    IN PREPLICA Replica,
    IN PCXTION  Cxtion,
    IN BOOL     Later
    );

VOID
RcsUpdateReplicaSetMember(
    IN PREPLICA Replica
    );

VOID
RcsJoinCxtion(
    IN PCOMMAND_PACKET  Cmd
    );

VOID
RcsEmptyPreExistingDir(
    IN PREPLICA Replica
    );

BOOL
RcsSetSysvolReady(
    IN DWORD    NewSysvolReady
    );

VOID
RcsReplicaSetRegistry(
    IN PREPLICA     Replica
    );


//
// Send Command server functions called from here.
//
VOID
SndCsSubmitCommPkt2(
    IN PREPLICA             Replica,
    IN PCXTION              Cxtion,
    IN PCHANGE_ORDER_ENTRY  Coe,
    IN BOOL                 SetTimeout,
    IN PCOMM_PACKET         CommPkt
    );

PCOMM_PACKET
CommBuildCommPkt(
    IN PREPLICA                 Replica,
    IN PCXTION                  Cxtion,
    IN ULONG                    Command,
    IN PGEN_TABLE               VVector,
    IN PCOMMAND_PACKET          Cmd,
    IN PCHANGE_ORDER_COMMAND    Coc
    );


BOOL
InitSyncPriorityClassSemanticsA(
    IN PREPLICA     Replica,
    IN DWORD        PriorityClass
    )
/*++
Routine Description:
    This function determines if the current state of the cxtions satisfies
    Semantics 'A' for priority class 'PriorityClass'
    
    This class is satisfied if all cxtions from this class have completed
    the initial sync.
    
Arguments:
    Replica       - Replica in question.
    PriorityClass - Priority class to evaluate for.

Return Value:
    TRUE if ok to move to next class, FALSE otherwise.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncPriorityClassSemanticsA:"

    PCXTION     Cxtion;
    PVOID       Key;
    PGEN_ENTRY  Entry;
    DWORD       NumInClass     = 0;
    DWORD       NumComplete    = 0;
    DWORD       NumCommError   = 0;
    DWORD       NumNotComplete = 0;


    DPRINT1(5,":IS: InitSyncPriorityClassSemanticsA called with priority %d\n", PriorityClass);

    Key = NULL;
    GTabLockTable(Replica->InitSyncCxtionsMasterList);
    while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {

        Cxtion = Entry->Data;            

        if (Cxtion->Priority > PriorityClass) {
            //
            // We have done evaluating the class in question.
            //
            break;
        } else if (Cxtion->Priority < PriorityClass) {
            continue;
        }

        do {
            Cxtion = Entry->Data;            

            DPRINT5(5, ":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n", 
                    Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
                    (CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
                    L"Init sync done"));

            NumInClass++;

            if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
                NumComplete++;
            } else if ((Cxtion->Penalty != 0) || 
                       (CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC))){
                //
                // Either there was some comm error trying to talk to this
                // partner or this partner has not sent us packets in a while.
                //
                NumCommError++;
                NumNotComplete++;
            } else {
                NumNotComplete++;
            }

            Entry = Entry->Dups;
        } while ( Entry != NULL );
    }

    GTabUnLockTable(Replica->InitSyncCxtionsMasterList);

    //
    // This class is satisfied if all cxtions from this class have completed
    // the initial sync.
    //
    if (NumInClass == NumComplete) {
        return TRUE;
    } else {
        return FALSE;
    }
}

BOOL
InitSyncPriorityClassSemanticsB(
    IN PREPLICA     Replica,
    IN DWORD        PriorityClass
    )
/*++
Routine Description:
    This function determines if the current state of the cxtions satisfies
    Semantics 'B' for priority class 'PriorityClass'
    
    This class is satisfied if atleast one cxtion from this class has completed
    the initial sync..

Arguments:
    Replica       - Replica in question.
    PriorityClass - Priority class to evaluate for.

Return Value:
    TRUE if ok to move to next class, FALSE otherwise.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncPriorityClassSemanticsB:"

    PCXTION     Cxtion;
    PVOID       Key;
    PGEN_ENTRY  Entry;
    DWORD       NumInClass     = 0;
    DWORD       NumComplete    = 0;
    DWORD       NumCommError   = 0;
    DWORD       NumNotComplete = 0;


    DPRINT1(5,":IS: InitSyncPriorityClassSemanticsB called with priority %d\n", PriorityClass);

    Key = NULL;
    GTabLockTable(Replica->InitSyncCxtionsMasterList);
    while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {

        Cxtion = Entry->Data;            

        if (Cxtion->Priority > PriorityClass) {
            //
            // We have done evaluating the class in question.
            //
            break;
        } else if (Cxtion->Priority < PriorityClass) {
            continue;
        }

        do {
            Cxtion = Entry->Data;            

            DPRINT5(5, " :IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n", 
                    Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
                    (CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
                    L"Init sync done"));

            NumInClass++;

            if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
                NumComplete++;
            } else if ((Cxtion->Penalty != 0) || 
                       (CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC))){
                //
                // Either there was some comm error trying to talk to this
                // partner or this partner has not sent us packets in a while.
                //
                NumCommError++;
                NumNotComplete++;
            } else {
                NumNotComplete++;
            }

            Entry = Entry->Dups;
        } while ( Entry != NULL );
    }

    GTabUnLockTable(Replica->InitSyncCxtionsMasterList);

    //
    // This class is satisfied if atleast one cxtion from this class has completed
    // the initial sync..
    //
    if ((NumInClass == 0) || (NumComplete >= 1)) {
        return TRUE;
    } else {
        return FALSE;
    }
}

BOOL
InitSyncPriorityClassSemanticsC(
    IN PREPLICA     Replica,
    IN DWORD        PriorityClass
    )
/*++
Routine Description:
    This function determines if the current state of the cxtions satisfies
    Semantics 'C' for priority class 'PriorityClass'
    
    This class is satisfied when all cxtions in this class have been attempted.    
    
Arguments:
    Replica       - Replica in question.
    PriorityClass - Priority class to evaluate for.

Return Value:
    TRUE if ok to move to next class, FALSE otherwise.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncPriorityClassSemanticsC:"

    PCXTION     Cxtion;
    PVOID       Key;
    PGEN_ENTRY  Entry;
    DWORD       NumInClass     = 0;
    DWORD       NumComplete    = 0;
    DWORD       NumCommError   = 0;
    DWORD       NumNotComplete = 0;


    DPRINT1(5,":IS: InitSyncPriorityClassSemanticsC called with priority %d\n", PriorityClass);

    Key = NULL;
    GTabLockTable(Replica->InitSyncCxtionsMasterList);
    while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {

        Cxtion = Entry->Data;            

        if (Cxtion->Priority > PriorityClass) {
            //
            // We have done evaluating the class in question.
            //
            break;
        } else if (Cxtion->Priority < PriorityClass) {
            continue;
        }

        do {
            Cxtion = Entry->Data;            

            DPRINT5(5, "Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n", 
                    Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
                    (CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
                    L"Init sync done"));

            NumInClass++;

            if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
                NumComplete++;
            } else if ((Cxtion->Penalty != 0) || 
                       (CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC))){
                //
                // Either there was some comm error trying to talk to this
                // partner or this partner has not sent us packets in a while.
                //
                NumCommError++;
                NumNotComplete++;
            } else {
                NumNotComplete++;
            }

            Entry = Entry->Dups;
        } while ( Entry != NULL );
    }

    GTabUnLockTable(Replica->InitSyncCxtionsMasterList);

    //
    // This class is satisfied when all cxtions in this class have been attempted.
    //
    if (NumInClass == (NumComplete + NumCommError)) {
        return TRUE;
    } else {
        return FALSE;
    }
}

BOOL
InitSyncPriorityClassSemanticsD(
    IN PREPLICA     Replica,
    IN DWORD        PriorityClass
    )
/*++
Routine Description:
    This function determines if the current state of the cxtions satisfies
    Semantics 'D' for priority class 'PriorityClass'
    
    
    This class is satisfied if atleast one cxtion has completed
    the initial sync..
    
    
Arguments:
    Replica       - Replica in question.
    PriorityClass - Priority class to evaluate for.

Return Value:
    TRUE if ok to move to next class, FALSE otherwise.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncPriorityClassSemanticsD:"

    PCXTION     Cxtion;
    PVOID       Key;
    PGEN_ENTRY  Entry;
    DWORD       NumTotal       = 0;
    DWORD       NumComplete    = 0;

    DPRINT1(5,":IS: InitSyncPriorityClassSemanticsB called with priority %d\n", PriorityClass);

    Key = NULL;
    GTabLockTable(Replica->InitSyncCxtionsMasterList);
    while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {

        do {
            Cxtion = Entry->Data;            

            DPRINT5(5, "Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n", 
                    Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
                    (CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
                    L"Init sync done"));

            NumTotal++;

            if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
                NumComplete++;
            }

            Entry = Entry->Dups;
        } while ( Entry != NULL );
    }

    GTabUnLockTable(Replica->InitSyncCxtionsMasterList);

    //
    // This class is satisfied if atleast one cxtion has completed
    // the initial sync..
    //
    if ((NumTotal == 0) || (NumComplete >= 1)) {
        return TRUE;
    } else {
        return FALSE;
    }
}

//
// This is a static array of functions that determines what rules are applied to
// what priority class. The rules are in form of functions above that return
// true or false depending on whether the rule is satisfied or not.
//
BOOL (*InitSyncPriorityClassSemantic [])(PREPLICA,DWORD) = {
    InitSyncPriorityClassSemanticsC,    // Rules for priority class 0
    InitSyncPriorityClassSemanticsA,    // Rules for priority class 1
    InitSyncPriorityClassSemanticsA,    // Rules for priority class 2
    InitSyncPriorityClassSemanticsB,    // Rules for priority class 3
    InitSyncPriorityClassSemanticsB,    // Rules for priority class 4
    InitSyncPriorityClassSemanticsC,    // Rules for priority class 5
    InitSyncPriorityClassSemanticsC,    // Rules for priority class 6
    InitSyncPriorityClassSemanticsC,    // Rules for priority class 7
    InitSyncPriorityClassSemanticsD     // Rules for priority class 8
};


BOOL
InitSyncIsPriorityClassSatisfied(
    IN PREPLICA     Replica,
    IN DWORD        PriorityClass
    )
/*++
Routine Description:
    Use the priority class semantics to decide what is the max class we can work on,

Arguments:
    Replica       - Replica in question.
    PriorityClass - Priority class to evaluate for.

Return Value:
    TRUE if ok to move to next class, FALSE otherwise.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncGetMaxAllowedPriority:"

    BOOL    (* PrioritySemanticFunction)(PREPLICA,DWORD);

    PrioritySemanticFunction = InitSyncPriorityClassSemantic[PriorityClass];

    return (*PrioritySemanticFunction)(Replica,PriorityClass);
}


DWORD
InitSyncGetMaxAllowedPriority(
    IN PREPLICA     Replica
    )
/*++
Routine Description:
    Use the priority class semantics to decide what is the max class we can work on,

Arguments:
    Replica       - Replica in question.

Return Value:
    Maximum allowed priority class.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncGetMaxAllowedPriority:"

    DWORD       MaxAllowedPriorityClass = 0;

    //
    // Evaluate if each priority is satisfied or not.
    //

    while ((MaxAllowedPriorityClass <= FRSCONN_MAX_PRIORITY) && 
           InitSyncIsPriorityClassSatisfied(Replica,MaxAllowedPriorityClass)) {
        ++MaxAllowedPriorityClass;
    }

    DPRINT2(4,":IS: Replica %ws - MaxAllowedPriorityClass = %d\n", 
            Replica->SetName->Name, MaxAllowedPriorityClass); 

    return MaxAllowedPriorityClass;
}


DWORD
InitSyncGetWorkingPriority(
    IN PREPLICA     Replica
    )
/*++
Routine Description:
    Get the current priority class we are working on,

Arguments:
    Replica.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncGetWorkingPriority:"

    DWORD       WorkingPriority = 0;
    PVOID       Key;
    PCXTION     Cxtion;

    Key = NULL;
    while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {
        DPRINT5(5, ":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n", 
                Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
                (CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
                L"Init sync done"));
        if (WorkingPriority < Cxtion->Priority) {
            WorkingPriority = Cxtion->Priority;
        }
    }

    DPRINT2(4,":IS: Replica %ws - CurrentWorkingPriorityClass = %d\n", 
            Replica->SetName->Name, WorkingPriority); 

    return WorkingPriority;
}

VOID
InitSyncCsSubmitTransfer(
    IN PCOMMAND_PACKET  Cmd,
    IN USHORT           Command
    )
/*++
Routine Description:
    Transfer a request to the initial sync controller command server.

Arguments:
    Cmd       - Command packet.
    Command   - Command to convert to.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncCsSubmitTransfer:"
    //
    // Submit a request to allocate staging area
    //
    Cmd->TargetQueue = RsReplica(Cmd)->InitSyncQueue;
    Cmd->Command = Command;
    RsTimeout(Cmd) = 0;
    DPRINT3(4,":IS: Transfer 0x%08x (0x%08x) to %ws\n",
            Command, Cmd, RsReplica(Cmd)->SetName->Name);
    FrsSubmitCommandServer(&InitSyncCs, Cmd);
}


VOID
InitSyncCmdPktCompletionRoutine(
    IN PCOMMAND_PACKET Cmd,
    IN PVOID           Arg
    )
/*++
Routine Description:
    Completion routine for InitSync command packets. Free some specific fields
    and send the command on to the generic command packet completion routine 
    for freeing.

Arguments:
    Cmd       - Command packet.
    Arg       - Cmd->CompletionArg

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncCmdPktCompletionRoutine:"

    DPRINT1(4, ":IS: InitSync completion 0x%08x\n", Cmd);

    FrsFreeGName(RsCxtion(Cmd));

    //
    // Send the packet on to the generic completion routine for freeing
    //
    FrsSetCompletionRoutine(Cmd, FrsFreeCommand, NULL);
    FrsCompleteCommand(Cmd, Cmd->ErrorStatus);

}


VOID
InitSyncSubmitToInitSyncCs(
    IN PREPLICA Replica,
    IN USHORT   Command
    )
/*++
Routine Description:
    Submit a command to a initial sync command server.

Arguments:
    Replica      - existing replica
    Command

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncSubmitToInitSyncCs:"
    PCOMMAND_PACKET Cmd;

    //
    // Allocate a command packet
    //
    Cmd = FrsAllocCommand(Replica->InitSyncQueue, Command);
    FrsSetCompletionRoutine(Cmd, InitSyncCmdPktCompletionRoutine, NULL);

    //
    // Address of replica set.
    //
    RsReplica(Cmd) = Replica;

    REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncSubmitToInitSyncCs cmd");
    FrsSubmitCommandServer(&InitSyncCs, Cmd);
}


VOID
InitSyncDelSubmitToInitSyncCs(
    IN PREPLICA Replica,
    IN PCXTION  Cxtion,
    IN USHORT   Command,
    IN DWORD    TimeoutInMilliseconds
    )
/*++
Routine Description:
    Submit a new command to the delayed command server to be submitted to the
    initial sync controller command server after "TimeoutInMilliseconds" ms.

Arguments:
    Replica      - existing replica
    Cxtion       - existing connection
    Command
    TimeoutInMilliseconds - This command will be submitted to the Init Sync
                            Command server after these many ms.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncDelSubmitToInitSyncCs:"
    PCOMMAND_PACKET Cmd;

    //
    // Allocate a command packet
    //
    Cmd = FrsAllocCommand(Replica->InitSyncQueue, Command);
    FrsSetCompletionRoutine(Cmd, InitSyncCmdPktCompletionRoutine, NULL);

    //
    // Address of replica set.
    //
    RsReplica(Cmd) = Replica;

    Cmd->TargetQueue = Replica->InitSyncQueue;

    //
    // CommPkts are used to detect init sync hangs.
    //
    if (Cxtion != NULL) {
        RsCxtion(Cmd) = FrsDupGName(Cxtion->Name);
        RsCommPkts(Cmd) = Cxtion->CommPkts;
    }

    RsTimeout(Cmd) = TimeoutInMilliseconds;
    //
    // This command will come back to us in in a little bit.
    //
    DPRINT2(4,":IS: Submit Cmd (0x%08x), Command 0x%88x\n", Cmd, Cmd->Command);

    FrsDelCsSubmitSubmit(&InitSyncCs, Cmd, RsTimeout(Cmd));
}


BOOL
InitSyncCsDelCsSubmit(
    IN PCOMMAND_PACKET  Cmd,
    IN USHORT           Command,
    IN DWORD            TimeoutInMilliseconds
    )
/*++
Routine Description:
    Submit this command to the delayed command server to be submitted to the
    initial sync controller command server after "TimeoutInMilliseconds" ms.

Arguments:
    Cmd
    Command
    TimeoutInMilliseconds - This command will be submitted to the Init Sync
                            Command server after these many ms.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncCsDelCsSubmit:"

    //
    // Extend the retry time (but not too long)
    //

    Cmd->Command = Command;
    RsTimeout(Cmd) = TimeoutInMilliseconds;
    //
    // This command will come back to us in in a little bit.
    //
    DPRINT2(4,":IS: Submit Cmd (0x%08x), Command 0x%08x\n", Cmd, Cmd->Command);

    FrsDelCsSubmitSubmit(&InitSyncCs, Cmd, RsTimeout(Cmd));
    return (TRUE);
}



VOID
InitSyncBuildMasterList(
    IN PREPLICA Replica
    )
/*++
Routine Description:
    Build the master list from the Replica->Cxtions table.

Arguments:
    Replica

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncBuildMasterList:"

    PCXTION     Cxtion  = NULL;
    PVOID       Key;

    FRS_ASSERT(Replica->InitSyncCxtionsMasterList != NULL);
    //
    // If we already have a master list then empty it,
    //
    GTabEmptyTable(Replica->InitSyncCxtionsMasterList,NULL);

    LOCK_CXTION_TABLE(Replica);

    //
    // Take all inbound connections and put them in the master list.
    //
    Key = NULL;
    while (Cxtion = GTabNextDatum(Replica->Cxtions, &Key)) {

        //
        // Skip the journal conneciton.
        //
        if (Cxtion->JrnlCxtion) {
            continue;
        }

        //
        // We are interested in inbound connections only.
        //
        if (!Cxtion->Inbound) {
            continue;
        }

        //
        // We clear the paused bit on connections that have completed the initial sync.
        // These connections are free to join anytime. We still need them in the master
        // list as they are needed to verify if the priority class is satisfied.
        //
        if (!CxtionFlagIs(Cxtion, CXTION_FLAGS_INIT_SYNC)) {
            if (CxtionFlagIs(Cxtion,CXTION_FLAGS_PAUSED)) {
                ClearCxtionFlag(Cxtion, CXTION_FLAGS_PAUSED); // In case it is still set.
                CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC Unpaused");
            }
        } else {
            //
            // Initially all connections in INIT_SYNC state are paused.
            //
            SetCxtionFlag(Cxtion, CXTION_FLAGS_PAUSED);
            CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC Paused");
        }


        GTabInsertEntry(Replica->InitSyncCxtionsMasterList, Cxtion, &Cxtion->Priority, NULL);
    }

    UNLOCK_CXTION_TABLE(Replica);
}


VOID
InitSyncBuildWorkingList(
    IN PREPLICA Replica,
    IN DWORD    PriorityClass,
    IN BOOL     ResetList
    )
/*++
Routine Description:
    Build the working list from the Replica->InitSyncCxtionsMasterList table.

Arguments:
    Replica
    PriorityClass - Max Priority Class to pull from master list.
    ResetList - Rebuild the working list.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncBuildWorkingList:"

    PCXTION     Cxtion  = NULL;
    PVOID       Key;
    PGEN_ENTRY  Entry;


    LOCK_CXTION_TABLE(Replica);

    //
    // If we already have a working list then empty it and create a new one,
    //
    if ((Replica->InitSyncCxtionsWorkingList != NULL) && ResetList) {

        GTabEmptyTable(Replica->InitSyncCxtionsWorkingList,NULL);
    }


    Key = NULL;
    GTabLockTable(Replica->InitSyncCxtionsMasterList);


    //
    // Take all connections from the master list that are <= PriorityClass and
    // put them on the current working list.
    //
    while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {

        Cxtion = Entry->Data;            

        if (Cxtion->Priority <= PriorityClass) {
            do {
                Cxtion = Entry->Data;            
                GTabInsertEntry(Replica->InitSyncCxtionsWorkingList, Cxtion, Cxtion->Name->Guid, NULL);
                Entry = Entry->Dups;
            } while ( Entry != NULL );
        }
    }

    GTabUnLockTable(Replica->InitSyncCxtionsMasterList);

    DPRINT1(4, ":IS: +++ Printing InitSyncCxtionsMasterList for Replica %ws\n", Replica->SetName->Name);
    Key = NULL;
    GTabLockTable(Replica->InitSyncCxtionsMasterList);
    while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {
        do {
            Cxtion = Entry->Data;            
            DPRINT5(4, ":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n", 
                    Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
                    (CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
                    L"Init sync done"));
            Entry = Entry->Dups;
        } while ( Entry != NULL );
    }
    GTabUnLockTable(Replica->InitSyncCxtionsMasterList);

    DPRINT1(4, ":IS: +++ Printing InitSyncCxtionsWorkingList for Replica %ws\n", Replica->SetName->Name);

    Key = NULL;
    while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {
        DPRINT5(4, ":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n", 
                Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
                (CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
                L"Init sync done"));
    }

    UNLOCK_CXTION_TABLE(Replica);

}


VOID
InitSyncStartSync(
    IN PCOMMAND_PACKET  Cmd
    )
/*++
Routine Description:
    Start the initial sync on this replica.

Arguments:
    Cmd.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncStartSync:"
    PREPLICA            Replica;
    PCXTION             Cxtion;
    PVOID               Key;
    DWORD               CxtionPriority;
    PGEN_ENTRY          Entry;
    DWORD               MaxAllowedPriority;

    Replica = RsReplica(Cmd);


    REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncStartSync entry");
    FRS_PRINT_TYPE(4, Replica);

    //
    // The replica has to be in SEEDING state to get this command.
    //

    FRS_ASSERT(BooleanFlagOn(Replica->CnfFlags, CONFIG_FLAG_SEEDING));

    //
    // If we have already started a initial sync on this replica then
    // nothing to do here.
    //
    if (Replica->InitSyncCxtionsMasterList != NULL) {
        return;
    }

    Replica->InitSyncCxtionsMasterList = GTabAllocNumberTable();
    Replica->InitSyncCxtionsWorkingList = GTabAllocTable();


    InitSyncBuildMasterList(Replica);

    MaxAllowedPriority = InitSyncGetMaxAllowedPriority(Replica);

    InitSyncBuildWorkingList(Replica,MaxAllowedPriority,TRUE);

    InitSyncCsSubmitTransfer(Cmd, CMD_INITSYNC_JOIN_NEXT);

    InitSyncSubmitToInitSyncCs(Replica,CMD_INITSYNC_KEEP_ALIVE);

    return;

}


VOID
InitSyncKeepAlive(
    IN PCOMMAND_PACKET  Cmd
    )
/*++
Routine Description:
    This command keeps coming back to us until all cxtions have completed the.
    initial sync. It is used to detect hangs.

Arguments:
    Cmd.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncKeepAlive:"
    PREPLICA        Replica;
    PVOID           Key;
    PCXTION         Cxtion;

    Replica = RsReplica(Cmd);


    REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncKeepAlive entry");

    //
    // Stop calling keepalive once we are out of seeding state.
    //
    if (!BooleanFlagOn(Replica->CnfFlags,CONFIG_FLAG_SEEDING)) {
        FrsCompleteCommand(Cmd, ERROR_SUCCESS);
        return;
    }

    //
    // Do not call JOIN_NEXT as long as there is one cxtion that is not paused and
    // not hung,
    //
    Key = NULL;
    while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {
        if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_PAUSED) &&
            CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) &&
            !CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC)) {
            DPRINT5(5, ":IS: Working on - Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n", 
                    Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
                    (CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
                    L"Init sync done"));
            goto RETURN;
        }
    }

    InitSyncSubmitToInitSyncCs(Replica,CMD_INITSYNC_JOIN_NEXT);

RETURN:

    InitSyncCsDelCsSubmit(Cmd, CMD_INITSYNC_KEEP_ALIVE, 240 * 1000);

    return;
}


VOID
InitSyncCompletedOnePass(
    IN PREPLICA     Replica
    )
/*++
Routine Description:
    Initial sync completed one pass for this replica. Take the appropriate action.
    Share out the sysvols here. 

Arguments:
    Cmd.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncCompletedOnePass:"


    DPRINT1(4,":IS: Replica %ws has successfully completed one pass of Seeding.\n",Replica->SetName->Name);

    SetFlag(Replica->CnfFlags, CONFIG_FLAG_ONLINE);
    Replica->NeedsUpdate = TRUE;
    RcsUpdateReplicaSetMember(Replica);

    //
    // Set sysvol ready to 1 if this is the sysvol replica set
    // and we haven't shared out the sysvol yet.
    //
    if (FRS_RSTYPE_IS_SYSVOL(Replica->ReplicaSetType) &&
        !Replica->IsSysvolReady) {
        RcsReplicaSetRegistry(Replica);
        Replica->IsSysvolReady = RcsSetSysvolReady(1);
    }

}


VOID
InitSyncCompleted(
    IN PREPLICA     Replica
    )
/*++
Routine Description:
    Initial sync completed for this replica. Take the appropriate action.
    Get out of seeding state.

Arguments:
    Cmd.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncCompleted:"


    ClearFlag(Replica->CnfFlags, CONFIG_FLAG_SEEDING);
    Replica->NeedsUpdate = TRUE;
    RcsUpdateReplicaSetMember(Replica);

    //
    // Half-hearted attempt to delete the empty directories from
    // the preexisting directory
    //
    RcsEmptyPreExistingDir(Replica);

    //
    // Cleanup the state associated with the InitSync state.
    //
    DPRINT1(4,":IS: Replica %ws has successfully completed Seeding.\n",Replica->SetName->Name);

    Replica->InitSyncCxtionsMasterList = GTabFreeTable(Replica->InitSyncCxtionsMasterList,NULL);
    Replica->InitSyncCxtionsWorkingList = GTabFreeTable(Replica->InitSyncCxtionsWorkingList,NULL);

}


VOID
InitSyncJoinNext(
    IN PCOMMAND_PACKET  Cmd
    )
/*++
Routine Description:
    Join the next in line.

Arguments:
    Cmd.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncJoinNext:"
    PREPLICA            Replica;
    PCXTION             Cxtion;
//    PCXTION             NewCxtion;
    PVOID               Key;
    DWORD               CxtionPriority;
    PGEN_ENTRY          Entry;
    DWORD               NumUnPaused;
    DWORD               MasterMaxAllowedPriority;
    DWORD               CurrentWorkingPriority;

    Replica = RsReplica(Cmd);

    REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncJoinNext entry");
    FRS_PRINT_TYPE(4, Replica);

    //
    // If the replica has already seeded then we do not have anything to process.
    //
    if (!BooleanFlagOn(Replica->CnfFlags, CONFIG_FLAG_SEEDING)) {
        FrsCompleteCommand(Cmd, ERROR_SUCCESS);
        return;
    }

    //
    // Get the maximum allowed priority by scanning the master list.
    // We need to do this because new connections might have been added to
    // the master list that have higher priority than the current working priority
    // of the working list.
    //
    MasterMaxAllowedPriority = InitSyncGetMaxAllowedPriority(Replica);
    CurrentWorkingPriority = InitSyncGetWorkingPriority(Replica);

    if (MasterMaxAllowedPriority < CurrentWorkingPriority) {
        //
        // One or more new connections with lower priority might have been added.
        // reset the working list to reflect that.
        //
        InitSyncBuildWorkingList(Replica,MasterMaxAllowedPriority,TRUE);

    } else if (MasterMaxAllowedPriority > CurrentWorkingPriority) {
        //
        // Connections from next priority class can be added to the working list.
        //
        InitSyncBuildWorkingList(Replica,MasterMaxAllowedPriority,FALSE);
    }

    if (MasterMaxAllowedPriority > FRSCONN_MAX_PRIORITY) {
        REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncJoinNext completed one pass");
        InitSyncCompletedOnePass(Replica);
    }

    LOCK_CXTION_TABLE(Replica);

    Key = NULL;
    NumUnPaused = 0;
    while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {

        //
        // If the cxtion has already completed the initial sync then skip it.
        //
        if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
            continue;
        }

        ++NumUnPaused;

        if (CxtionFlagIs(Cxtion,CXTION_FLAGS_PAUSED)) {
            ClearCxtionFlag(Cxtion, CXTION_FLAGS_PAUSED);
            CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC Unpaused");
        }

        if (CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC)) {
            ClearCxtionFlag(Cxtion, CXTION_FLAGS_HUNG_INIT_SYNC);
            CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC clear hung state");
        }

        CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC sending NEED_JOIN");

        //
        // Submit a cmd with the replica command server to send need_join over this
        // cxtion,
        // 
        RcsSubmitReplicaCxtionJoin(Replica, Cxtion, FALSE);
        //
        // Submit a cmd with the init sync command server to track the progress of this
        // cxtion. This cmd will look for hung init sync.
        // 
        InitSyncDelSubmitToInitSyncCs(Replica,Cxtion,CMD_INITSYNC_CHECK_PROGRESS,300*1000);
    }

    UNLOCK_CXTION_TABLE(Replica);

    //
    // Done synching all connections. Reset seeding state.
    //
    if (NumUnPaused == 0) {

        InitSyncCompleted(Replica);
        RcsSubmitTransferToRcs(Cmd, CMD_VVJOIN_DONE_UNJOIN);

    } else {
        FrsCompleteCommand(Cmd, ERROR_SUCCESS);
    }
}


VOID
InitSyncCheckProgress(
    IN PCOMMAND_PACKET  Cmd
    )
/*++
Routine Description:
    Check the progress of this connectio. Put it in hung state if no packets have
    been received on this connection for some time.

Arguments:
    Cmd.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncCheckProgress:"
    PREPLICA            Replica;
    PCXTION             InCxtion;

    Replica = RsReplica(Cmd);

    //
    // Retire this command if we are out of seeding state.
    //
    if (!BooleanFlagOn(Replica->CnfFlags,CONFIG_FLAG_SEEDING)) {
        FrsCompleteCommand(Cmd, ERROR_SUCCESS);
        return;
    }

    LOCK_CXTION_TABLE(Replica);

    REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncCheckProgress entry");

    //
    // Find and check the cxtion
    //
    InCxtion = GTabLookupNoLock(Replica->Cxtions, RsCxtion(Cmd)->Guid, NULL);

    if (InCxtion != NULL) {
        DPRINT5(5,":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n", 
                InCxtion->Priority,InCxtion->Penalty, InCxtion->CommPkts, InCxtion->Name->Name,
                (CxtionFlagIs(InCxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
                L"Init sync done"));
    }

    //
    // Retire this command if the cxtion has been paused or if the connection is
    // out of seeding state.
    //
    if (!InCxtion || CxtionFlagIs(InCxtion,CXTION_FLAGS_PAUSED) ||
        !CxtionFlagIs(InCxtion,CXTION_FLAGS_INIT_SYNC)) {

        if (CxtionFlagIs(InCxtion,CXTION_FLAGS_HUNG_INIT_SYNC)) {
            ClearCxtionFlag(InCxtion,CXTION_FLAGS_HUNG_INIT_SYNC);
            CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC clear hung state");
        }

        FrsCompleteCommand(Cmd, ERROR_SUCCESS);
        goto RETURN;
    }

    //
    // Do not declare hung if the schedule is off and we are asked to follow schedule.
    //
    if (!NTDSCONN_IGNORE_SCHEDULE(InCxtion->Options) &&
        CxtionFlagIs(InCxtion,CXTION_FLAGS_SCHEDULE_OFF)){
        InitSyncCsDelCsSubmit(Cmd,CMD_INITSYNC_CHECK_PROGRESS,300*1000);
        goto RETURN;
    }

    if (RsCommPkts(Cmd) != InCxtion->CommPkts) {
        RsCommPkts(Cmd) = InCxtion->CommPkts;
        InitSyncCsDelCsSubmit(Cmd,CMD_INITSYNC_CHECK_PROGRESS,300*1000);
    } else {
        SetCxtionFlag(InCxtion,CXTION_FLAGS_HUNG_INIT_SYNC);
        CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC Hung");
        FrsCompleteCommand(Cmd, ERROR_SUCCESS);
    }

RETURN:
    UNLOCK_CXTION_TABLE(Replica);
    return;
}


VOID
InitSyncStartJoin(
    IN PCOMMAND_PACKET  Cmd
    )
/*++
Routine Description:
    We have received start join from a inbound partner. We send need joins to all the
    connections in the current class. When we get start_join from one of the partners
    we pick that one to vvjoin with first and put the remaining connections in the 
    paused state.

Arguments:
    Cmd.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncStartJoin:"
    PREPLICA            Replica;
    PCXTION             InCxtion;
    PCXTION             Cxtion;
    PCXTION             NewCxtion;
    PVOID               Key;

    Replica = RsReplica(Cmd);

    LOCK_CXTION_TABLE(Replica);

    REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncStartJoin entry");
    FRS_PRINT_TYPE(4, Replica);

    //
    // Find and check the cxtion
    //
    InCxtion = GTabLookupNoLock(Replica->Cxtions, RsCxtion(Cmd)->Guid, NULL);

    if (!InCxtion) {
        return;
    }

    //
    // Ignore the start join if this connection is paused.
    //
    if (CxtionFlagIs(InCxtion,CXTION_FLAGS_PAUSED)) {

        CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, Paused - ignore start join");
        FrsCompleteCommand(Cmd, ERROR_SUCCESS);
        UNLOCK_CXTION_TABLE(Replica);

    } else {
        //
        // If we were waiting for our partner to respond or think we
        // have already joined then either start the join process or
        // resend our join info.
        //
        if (CxtionStateIs(InCxtion, CxtionStateUnjoined) ||
            CxtionStateIs(InCxtion, CxtionStateJoined)) {
            if (CxtionStateIs(InCxtion, CxtionStateUnjoined)) {
                SetCxtionState(InCxtion, CxtionStateStart);
            }
            //
            // Start the join process or resend the join info
            //
            // This is the first inbound partner that responded to the
            // NEED_JOIN. Start vvjoin with this inbound partner and 
            // pause all other cxtions in the working list that are still in
            // INIT_SYNC state.
            //

            Key = NULL;
            while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {
                DPRINT2(4, "P - %d : %ws\n", Cxtion->Priority, Cxtion->Name->Name);
                FRS_PRINT_TYPE(4, Cxtion);

                NewCxtion = GTabLookup(Replica->Cxtions,Cxtion->Name->Guid,NULL);
                if (NewCxtion != NULL) {
                    if (!GUIDS_EQUAL(NewCxtion->Name->Guid,InCxtion->Name->Guid) &&
                        CxtionFlagIs(NewCxtion,CXTION_FLAGS_INIT_SYNC)) {
                        SetCxtionFlag(NewCxtion,CXTION_FLAGS_PAUSED);
                        CXTION_STATE_TRACE(3, NewCxtion, Replica, 0, "F, INITSYNC Paused");
                    }
                }
            }

            //
            // Clear the CXTION_FLAGS_HUNG_INIT_SYNC flag if it was set.
            //
            if (CxtionFlagIs(InCxtion,CXTION_FLAGS_HUNG_INIT_SYNC)) {
                ClearCxtionFlag(InCxtion, CXTION_FLAGS_HUNG_INIT_SYNC);
                CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC clear hung state");
            }


            UNLOCK_CXTION_TABLE(Replica);

            CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, RcsJoinCxtion call");
            RcsJoinCxtion(Cmd);
        } else {
            UNLOCK_CXTION_TABLE(Replica);

            CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, Cannot start join");
            FrsCompleteCommand(Cmd, ERROR_SUCCESS);
        }
    }

}


VOID
InitSyncVvJoinDone(
    IN PCOMMAND_PACKET  Cmd
    )
/*++
Routine Description:
    vvjoin is done join next one.

Arguments:
    Cmd.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncVvJoinDone:"
    PREPLICA            Replica;
    PCXTION             InCxtion;
    PCOMM_PACKET        CPkt;

    Replica = RsReplica(Cmd);

    LOCK_CXTION_TABLE(Replica);

    //
    // Find and check the cxtion
    //
    InCxtion = GTabLookupNoLock(Replica->Cxtions, RsCxtion(Cmd)->Guid, NULL);

    if (!InCxtion) {
        return;
    }

    CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC complete");

    //
    // This connection has completed initial sync.
    // It is OK to join this connection at will now. 
    //
    if (CxtionFlagIs(InCxtion,CXTION_FLAGS_PAUSED)) {
        ClearCxtionFlag(InCxtion, CXTION_FLAGS_PAUSED);
        CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC Unpaused");
    }
    ClearCxtionFlag(InCxtion, CXTION_FLAGS_INIT_SYNC);

    //
    // If this is volatile connection unjoin our upstream partner.
    //
    //
    if (CxtionFlagIs(InCxtion, CXTION_FLAGS_VOLATILE)) {
        CPkt = CommBuildCommPkt(Replica, InCxtion, CMD_UNJOIN_REMOTE, NULL, NULL, NULL);
        SndCsSubmitCommPkt2(Replica, InCxtion, NULL, FALSE, CPkt);
    }

    UNLOCK_CXTION_TABLE(Replica);

    InitSyncCsSubmitTransfer(Cmd,CMD_INITSYNC_JOIN_NEXT);

}


DWORD
MainInitSyncCs(
    PVOID  Arg
    )
/*++
Routine Description:
    Entry point for a thread serving the initial sync controller command server.

Arguments:
    Arg - thread

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "MainInitSyncCs:"
    DWORD               WStatus = ERROR_SUCCESS;
    PCOMMAND_PACKET     Cmd;
    PFRS_QUEUE          IdledQueue;
    PFRS_THREAD         FrsThread = (PFRS_THREAD)Arg;

    //
    // Thread is pointing at the correct command server
    //
    FRS_ASSERT(FrsThread->Data == &InitSyncCs);
    FrsThread->Exit = ThSupExitWithTombstone;

    DPRINT(4, "Initial Sync command server has started.\n");

    //
    // Try-Finally
    //
    try {

        //
        // Capture exception.
        //
        try {

            //
            // Pull entries off the queue and process them
            //
cant_exit_yet:
            while (Cmd = FrsGetCommandServerIdled(&InitSyncCs, &IdledQueue)) {

                switch (Cmd->Command) {

                case CMD_INITSYNC_START_SYNC:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    InitSyncStartSync(Cmd);
                    break;

                case CMD_INITSYNC_JOIN_NEXT:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    InitSyncJoinNext(Cmd);
                    break;

                case CMD_INITSYNC_START_JOIN:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    InitSyncStartJoin(Cmd);
                    break;

                case CMD_INITSYNC_VVJOIN_DONE:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    InitSyncVvJoinDone(Cmd);
                    break;

                case CMD_INITSYNC_KEEP_ALIVE:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    InitSyncKeepAlive(Cmd);
                    break;

                case CMD_INITSYNC_CHECK_PROGRESS:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    InitSyncCheckProgress(Cmd);
                    break;

                case CMD_INITSYNC_UNJOIN:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    REPLICA_STATE_TRACE(3, Cmd, RsReplica(Cmd), 0, "F, processing");
                    InitSyncCsDelCsSubmit(Cmd, CMD_INITSYNC_JOINED,10*1000);
                    break;

                case CMD_INITSYNC_JOINED:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    REPLICA_STATE_TRACE(3, Cmd, RsReplica(Cmd), 0, "F, processing");
                    InitSyncCsDelCsSubmit(Cmd, CMD_INITSYNC_COMM_TIMEOUT,10*1000);
                    break;

                case CMD_INITSYNC_COMM_TIMEOUT:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    REPLICA_STATE_TRACE(3, Cmd, RsReplica(Cmd), 0, "F, processing");
                    break;

                default:
                    DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
                    FrsCompleteCommand(Cmd, ERROR_INVALID_FUNCTION);
                    break;
                }
                FrsRtlUnIdledQueue(IdledQueue);
            }
            //
            // Exit
            //
            FrsExitCommandServer(&InitSyncCs, FrsThread);
            goto cant_exit_yet;

        //
        // Get exception status.
        //
        } except (EXCEPTION_EXECUTE_HANDLER) {
            GET_EXCEPTION_CODE(WStatus);
        }


    } finally {

        if (WIN_SUCCESS(WStatus)) {
            if (AbnormalTermination()) {
                WStatus = ERROR_OPERATION_ABORTED;
            }
        }

        DPRINT_WS(4, "MainInitSyncCs finally.", WStatus);

        //
        // Trigger FRS shutdown if we terminated abnormally.
        //
        if (!WIN_SUCCESS(WStatus)) {
            DPRINT(4, "MainInitSyncCs terminated abnormally, forcing service shutdown.\n");
            FrsIsShuttingDown = TRUE;
            SetEvent(ShutDownEvent);
        }
    }

    return (WStatus);
}


VOID
InitSyncCsInitialize(
    VOID
    )
/*++
Routine Description:
    Initialize the Initial Sync Controller command server.

Arguments:
    None.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "InitSyncCsInitialize:"
    //
    // Initialize the command server
    //

    CfgRegReadDWord(FKC_MAX_INITSYNCCS_THREADS, NULL, 0, &MaxInitSyncCsThreads);

    FrsInitializeCommandServer(&InitSyncCs, MaxInitSyncCsThreads, L"InitSyncCs", MainInitSyncCs);
}





VOID
ShutDownInitSyncCs(
    VOID
    )
/*++
Routine Description:
    Shutdown the Initial Sync Controller command server.

Arguments:
    None.

Return Value:
    None.
--*/
{
#undef DEBSUB
#define DEBSUB  "ShutDownInitSyncCs:"

    PVOID       Key;
    PREPLICA    Replica;

    //
    // Rundown all known queues. New queue entries will bounce.
    //
    Key = NULL;
    while (Replica = GTabNextDatum(ReplicasByGuid, &Key)) {
        REPLICA_STATE_TRACE(3, NULL, Replica, 0, "F, Rundown InitSync cmd srv");
        if (Replica->InitSyncQueue != NULL) {
            FrsRunDownCommandServer(&InitSyncCs, Replica->InitSyncQueue);
        }
    }

    FrsRunDownCommandServer(&InitSyncCs, &InitSyncCs.Queue);
}