//----------------------------------------------------------------------------
//
// Starts a process server and sleeps forever.
//
// Copyright (C) Microsoft Corporation, 2000.
//
//----------------------------------------------------------------------------

#include <stdio.h>

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#define _ADVAPI32_
#define _KERNEL32_
#include <windows.h>
#define INITGUID
#include <objbase.h>
#define NOEXTAPI
#include <wdbgexts.h>
#include <ntdbg.h>

#include <cmnutil.hpp>
#include <dllimp.h>
#include <dbgrpc.hpp>
#include <dbgsvc.h>
#include <dbgsvc.hpp>

// The .CRT section is generated when static intializers,
// such as global class instances, exist.  It needs to
// be merged into .data to avoid a linker warning.
#pragma comment(linker, "/merge:.CRT=.data")

void DECLSPEC_NORETURN
PanicVa(HRESULT Status, char* Format, va_list Args)
{
    char Msg[256];

    _vsnprintf(Msg, sizeof(Msg), Format, Args);
    DbgPrint("Error 0x%08X: %s\n", Status, Msg);
    NtTerminateProcess(NtCurrentProcess(), (NTSTATUS)Status);
}

void DECLSPEC_NORETURN
Panic(HRESULT Status, char* Format, ...)
{
    va_list Args;

    va_start(Args, Format);
    PanicVa(Status, Format, Args);
    va_end(Args);
}

#if DBG
void
DbgAssertionFailed(PCSTR File, int Line, PCSTR Str)
{
    Panic(E_FAIL, "Assertion failed: %s(%d)\n  %s\n",
          File, Line, Str);
}
#endif

//----------------------------------------------------------------------------
//
// Proxy and stub support.
//
//----------------------------------------------------------------------------

// Generated headers.
#include "dbgsvc_p.hpp"
#include "dbgsvc_s.hpp"

void
DbgRpcInitializeClient(void)
{
    DbgRpcInitializeStubTables_dbgsvc(DBGRPC_SIF_DBGSVC_FIRST);
}
    
DbgRpcStubFunction
DbgRpcGetStub(USHORT StubIndex)
{
    USHORT If = (USHORT) DBGRPC_STUB_INDEX_INTERFACE(StubIndex);
    USHORT Mth = (USHORT) DBGRPC_STUB_INDEX_METHOD(StubIndex);
    DbgRpcStubFunctionTable* Table;

    if (If >= DBGRPC_SIF_DBGSVC_FIRST &&
        If >= DBGRPC_SIF_DBGSVC_LAST)
    {
        Table = g_DbgRpcStubs_dbgsvc;
        If -= DBGRPC_SIF_DBGSVC_FIRST;
    }
    else
    {
        return NULL;
    }
    if (Mth >= Table[If].Count)
    {
        return NULL;
    }

    return Table[If].Functions[Mth];
}

#if DBG
PCSTR
DbgRpcGetStubName(USHORT StubIndex)
{
    USHORT If = (USHORT) DBGRPC_STUB_INDEX_INTERFACE(StubIndex);
    USHORT Mth = (USHORT) DBGRPC_STUB_INDEX_METHOD(StubIndex);
    DbgRpcStubFunctionTable* Table;
    PCSTR** Names;

    if (If >= DBGRPC_SIF_DBGSVC_FIRST &&
        If >= DBGRPC_SIF_DBGSVC_LAST)
    {
        Table = g_DbgRpcStubs_dbgsvc;
        Names = g_DbgRpcStubNames_dbgsvc;
        If -= DBGRPC_SIF_DBGSVC_FIRST;
    }
    else
    {
        return "!InvalidInterface!";
    }
    if (Mth >= Table[If].Count)
    {
        return "!InvalidStubIndex!";
    }

    return Names[If][Mth];
}
#endif // #if DBG

HRESULT
DbgRpcPreallocProxy(REFIID InterfaceId, PVOID* Interface,
                    DbgRpcProxy** Proxy, PULONG IfUnique)
{
    return DbgRpcPreallocProxy_dbgsvc(InterfaceId, Interface,
                                      Proxy, IfUnique);
}

void
DbgRpcDeleteProxy(class DbgRpcProxy* Proxy)
{
    // All proxies used here are similar simple single
    // vtable proxy objects so IDebugClient can represent them all.
    delete (ProxyIUserDebugServices*)Proxy;
}

HRESULT
DbgRpcServerThreadInitialize(void)
{
    // Nothing to do.
    return S_OK;
}

void
DbgRpcServerThreadUninitialize(void)
{
    // Nothing to do.
}

void
DbgRpcError(char* Format, ...)
{
    va_list Args;

    va_start(Args, Format);
    PanicVa(E_FAIL, Format, Args);
    va_end(Args);
}

DBGRPC_SIMPLE_FACTORY(LiveUserDebugServices, __uuidof(IUserDebugServices), \
                      "Remote Process Server", (TRUE))
LiveUserDebugServicesFactory g_LiveUserDebugServicesFactory;

#ifdef  _M_IA64

#pragma section(".CRT$XCA",long,read)
#pragma section(".CRT$XCZ",long,read)
#pragma section(".CRT$XIA",long,read)
#pragma section(".CRT$XIZ",long,read)

#define _CRTALLOC(x) __declspec(allocate(x))

#else   /* ndef _M_IA64 */

#define _CRTALLOC(x)

#endif  /* ndef _M_IA64 */

typedef void (__cdecl *_PVFV)(void);

extern "C"
{

// C initializers collect here.
#pragma data_seg(".CRT$XIA")
_CRTALLOC(".CRT$XIA") _PVFV __xi_a[] = { NULL };
#pragma data_seg(".CRT$XIZ")
_CRTALLOC(".CRT$XIZ") _PVFV __xi_z[] = { NULL };
    
// C++ initializers collect here.
#pragma data_seg(".CRT$XCA")
_CRTALLOC(".CRT$XCA") _PVFV __xc_a[] = { NULL };
#pragma data_seg(".CRT$XCZ")
_CRTALLOC(".CRT$XCZ") _PVFV __xc_z[] = { NULL };

};

void __cdecl
_initterm (_PVFV * pfbegin, _PVFV * pfend)
{
    /*
     * walk the table of function pointers from the bottom up, until
     * the end is encountered.  Do not skip the first entry.  The initial
     * value of pfbegin points to the first valid entry.  Do not try to
     * execute what pfend points to.  Only entries before pfend are valid.
     */
    while ( pfbegin < pfend )
    {
        /*
         * if current table entry is non-NULL, call thru it.
         */
        if ( *pfbegin != NULL )
        {
            (**pfbegin)();
        }
        ++pfbegin;
    }
}

void __cdecl
main(int Argc, char** Argv)
{
    PSTR AppName;
    PSTR Options;
    HRESULT Status;

    // Manually invoke C and C++ initializers.
    _initterm( __xi_a, __xi_z );
    _initterm( __xc_a, __xc_z );

    AppName = Argv[0];
    
    while (--Argc > 0)
    {
        Argv++;

        break;
    }

    if (Argc != 1)
    {
        Panic(E_INVALIDARG, "Usage: dbgsrv <transport>");
    }

    Options = *Argv;

    DbgPrint("Running %s with '%s'\n", AppName, Options);

    if ((Status = InitDynamicCalls(&g_NtDllCallsDesc)) != S_OK)
    {
        Panic(Status, "InitDynamicCalls");
    }
    
    ULONG Flags;
    
    if ((Status = g_LiveUserDebugServices.Initialize(&Flags)) != S_OK)
    {
        Panic(Status, "LiveUserDebugServices::Initialize");
    }
    
    if ((Status = DbgRpcCreateServer(Options,
                                     &g_LiveUserDebugServicesFactory)) != S_OK)
    {
        Panic(Status, "StartProcessServer");
    }

    for (;;)
    {
        Sleep(1000);
            
        if (g_UserServicesUninitialized)
        {
            break;
        }
    }

    DbgRpcDeregisterServers();
}