/* 
 * Copyright (c) Microsoft Corporation
 *         
 * Module Name : 
 *             unlo.c
 *         
 * Shut down and delete functions
 * Where possible, code has been obtained from BINL server.
 *         
 * Sadagopan Rajaram -- Oct 14, 1999
 *         
 */        
 
#include "tcsrv.h"
#include <ntddser.h>
#include "tcsrvc.h"
#include "proto.h"

NTSTATUS
DeleteComPort(
    LPTSTR device
    )
/*++ 
    Deletes a Com port from the list 
--*/
{

    BOOL ret;
    NTSTATUS Status;
    PCOM_PORT_INFO pPrev,pComPortInfo;
    HANDLE Thread;
    int index,i;

    EnterCriticalSection(&GlobalMutex);
    if(TCGlobalServiceStatus.dwCurrentState == SERVICE_STOP_PENDING){
        // Entire Service is shutting down.
        LeaveCriticalSection(&GlobalMutex);
        return STATUS_SUCCESS;
    }   // find the device needed to be deleted.
    pComPortInfo = FindDevice(device,&index); 
    if(!pComPortInfo){
        // Bah ! give me an existing device.
        LeaveCriticalSection(&GlobalMutex);
        return (STATUS_OBJECT_NAME_NOT_FOUND);
    }
    // Set the terminate event on the com port.
    ret = SetEvent(pComPortInfo->Events[3]);
    Thread = Threads[index];
    LeaveCriticalSection(&GlobalMutex);
    // wait for the com port thread to finish.
    Status = NtWaitForSingleObject(Thread, FALSE, NULL);
    if (Status == WAIT_FAILED) {
        // catastrophe
        return Status;
    }
    EnterCriticalSection(&GlobalMutex);
    // do this again as another delete or insert may have 
    // changed the index, though how is beyond me :-) 
    // if we are already shutting down the service.
    if(TCGlobalServiceStatus.dwCurrentState == SERVICE_STOP_PENDING){
        // Entire Service is shutting down.
        LeaveCriticalSection(&GlobalMutex);
        return STATUS_SUCCESS;
    }
    pComPortInfo = FindDevice(device,&index);
    if(!pComPortInfo){
        LeaveCriticalSection(&GlobalMutex);
        return (STATUS_OBJECT_NAME_NOT_FOUND);
    }
    if(pComPortInfo == ComPortInfo){
        ComPortInfo = pComPortInfo->Next;
    }
    else{
        pPrev = ComPortInfo;
        while(pPrev->Next != pComPortInfo){// Can never fail
            pPrev = pPrev->Next;
        }
        pPrev->Next = pComPortInfo->Next;
    }
    pComPortInfo->Next = NULL;
    FreeComPortInfo(pComPortInfo);
    NtClose(Threads[index]);
    for(i=index;i<ComPorts-1;i++){
        // move the threads array to the proper place
        Threads[i]=Threads[i+1];
    }
    ComPorts --;
    if(ComPorts == 0){
        TCFree(Threads);
        Threads=NULL;
    }
    LeaveCriticalSection(&GlobalMutex);
    return(STATUS_SUCCESS);

}

VOID 
Shutdown(
    NTSTATUS Status
    )
/*++
    Cleanly shut down the service. delete all threads, cancel all outstanding IRPs.
    Close all open sockets. 
--*/ 
{
    PCOM_PORT_INFO pTemp;
    int i;

    SetEvent(TerminateService); // all threads down
    // Can do this another way,
    // We can take each comport device and
    // delete it using the DeleteComPort 
    // function. But, this allows for maximum 
    // parallelism even in shutting down :-)

    if(Threads){
        WaitForMultipleObjects(ComPorts,Threads, TRUE, INFINITE); 
        // BUGBUG - what if thread is a rougue thread and
        // never comes back. Must use some reasonable
        // time out. 
        // Theory says INFINITE is the safest :-)
    }
    
    //All threads terminated.
    // Now start freeing all global memory
    // just using the locks as a safety measure.
    EnterCriticalSection(&GlobalMutex);
    while(ComPortInfo){
        pTemp = ComPortInfo;
        ComPortInfo=pTemp->Next;
        pTemp->Next = NULL;
        FreeComPortInfo(pTemp);

    }
    TCFree(Threads);         
    NtClose(TerminateService);
    LeaveCriticalSection(&GlobalMutex);

    UNINITIALIZE_TRACE_MEMORY
    //All done, now print status and exit.
    TCGlobalServiceStatus.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus(TCGlobalServiceStatusHandle, &TCGlobalServiceStatus);

    TCDebugPrint(("Shutdown Status = %lx\n",Status));
    return;
}