/** @file
 *
 *  Definition of SVC Helper Process control routines.
 */

/*
 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 USA or visit http://www.sun.com if you need
 * additional information or have any questions.
 */

#include "svchlp.h"

//#include "HostImpl.h"
#include "Logging.h"

#include <VBox/err.h>

int netIfNetworkInterfaceHelperServer (SVCHlpClient *aClient,
                                        SVCHlpMsg::Code aMsgCode);

using namespace com;

enum { PipeBufSize = 1024 };

////////////////////////////////////////////////////////////////////////////////

/**
 *  GetLastError() is known to return NO_ERROR even after the Win32 API
 *  function (i.e. Write() to a non-connected server end of a pipe) returns
 *  FALSE... This method ensures that at least VERR_GENERAL_FAILURE is returned
 *  in cases like that. Intended to be called immediately after a failed API
 *  call.
 */
static inline int rtErrConvertFromWin32OnFailure()
{
    DWORD err = GetLastError();
    return err == NO_ERROR ? VERR_GENERAL_FAILURE
                           : RTErrConvertFromWin32 (err);
}

////////////////////////////////////////////////////////////////////////////////

SVCHlpClient::SVCHlpClient()
    : mIsOpen (false), mIsServer (false)
    , mReadEnd (NULL), mWriteEnd (NULL)
{
}

SVCHlpClient::~SVCHlpClient()
{
    close();
}

int SVCHlpClient::create (const char *aName)
{
    AssertReturn(aName, VERR_INVALID_PARAMETER);

    if (mIsOpen)
        return VERR_WRONG_ORDER;

    Bstr pipeName = Utf8StrFmt ("\\\\.\\pipe\\%s", aName);

    HANDLE pipe = CreateNamedPipe (pipeName,
                                   PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
                                   PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
                                   1, // PIPE_UNLIMITED_INSTANCES,
                                   PipeBufSize, PipeBufSize,
                                   NMPWAIT_USE_DEFAULT_WAIT,
                                   NULL);

    if (pipe == INVALID_HANDLE_VALUE)
        rtErrConvertFromWin32OnFailure();

    mIsOpen = true;
    mIsServer = true;
    mReadEnd = pipe;
    mWriteEnd = pipe;
    mName = aName;

    return VINF_SUCCESS;
}

int SVCHlpClient::open (const char *aName)
{
    AssertReturn(aName, VERR_INVALID_PARAMETER);

    if (mIsOpen)
        return VERR_WRONG_ORDER;

    Bstr pipeName = Utf8StrFmt ("\\\\.\\pipe\\%s", aName);

    HANDLE pipe = CreateFile (pipeName,
                              GENERIC_READ | GENERIC_WRITE,
                              0,
                              NULL,
                              OPEN_EXISTING,
                              0,
                              NULL);

    if (pipe == INVALID_HANDLE_VALUE)
        rtErrConvertFromWin32OnFailure();

    mIsOpen = true;
    mIsServer = false;
    mReadEnd = pipe;
    mWriteEnd = pipe;
    mName = aName;

    return VINF_SUCCESS;
}

int SVCHlpClient::connect()
{
    if (!mIsOpen || !mIsServer)
        return VERR_WRONG_ORDER;

    BOOL ok = ConnectNamedPipe (mReadEnd, NULL);
    if (!ok && GetLastError() != ERROR_PIPE_CONNECTED)
        rtErrConvertFromWin32OnFailure();

    return VINF_SUCCESS;
}

int SVCHlpClient::close()
{
    if (!mIsOpen)
        return VERR_WRONG_ORDER;

    if (mWriteEnd != NULL && mWriteEnd != mReadEnd)
    {
        if (!CloseHandle (mWriteEnd))
            rtErrConvertFromWin32OnFailure();
        mWriteEnd = NULL;
    }

    if (mReadEnd != NULL)
    {
        if (!CloseHandle (mReadEnd))
            rtErrConvertFromWin32OnFailure();
        mReadEnd = NULL;
    }

    mIsOpen = false;
    mIsServer = false;
    mName.setNull();

    return VINF_SUCCESS;
}

int SVCHlpClient::write (const void *aVal, size_t aLen)
{
    AssertReturn(aVal != NULL, VERR_INVALID_PARAMETER);
    AssertReturn(aLen != 0, VERR_INVALID_PARAMETER);

    if (!mIsOpen)
        return VERR_WRONG_ORDER;

    DWORD written = 0;
    BOOL ok = WriteFile (mWriteEnd, aVal, (ULONG)aLen, &written, NULL);
    AssertReturn(!ok || written == aLen, VERR_GENERAL_FAILURE);
    return ok ? VINF_SUCCESS : rtErrConvertFromWin32OnFailure();
}

int SVCHlpClient::write (const Utf8Str &aVal)
{
    if (!mIsOpen)
        return VERR_WRONG_ORDER;

    /* write -1 for NULL strings */
    if (aVal.isEmpty())
        return write ((size_t) ~0);

    size_t len = aVal.length();

    /* write string length */
    int vrc = write (len);
    if (RT_SUCCESS(vrc))
    {
        /* write string data */
        vrc = write (aVal.raw(), len);
    }

    return vrc;
}

int SVCHlpClient::write (const Guid &aGuid)
{
    Utf8Str guidStr = aGuid.toString();
    return write (guidStr);
}

int SVCHlpClient::read (void *aVal, size_t aLen)
{
    AssertReturn(aVal != NULL, VERR_INVALID_PARAMETER);
    AssertReturn(aLen != 0, VERR_INVALID_PARAMETER);

    if (!mIsOpen)
        return VERR_WRONG_ORDER;

    DWORD read = 0;
    BOOL ok = ReadFile (mReadEnd, aVal, (ULONG)aLen, &read, NULL);
    AssertReturn(!ok || read == aLen, VERR_GENERAL_FAILURE);
    return ok ? VINF_SUCCESS : rtErrConvertFromWin32OnFailure();
}

int SVCHlpClient::read (Utf8Str &aVal)
{
    if (!mIsOpen)
        return VERR_WRONG_ORDER;

    size_t len = 0;

    /* read string length */
    int vrc = read (len);
    if (RT_FAILURE(vrc))
        return vrc;

    /* length -1 means a NULL string */
    if (len == (size_t) ~0)
    {
        aVal.setNull();
        return VINF_SUCCESS;
    }

    aVal.reserve(len + 1);
    aVal.mutableRaw() [len] = 0;

    /* read string data */
    vrc = read (aVal.mutableRaw(), len);

    return vrc;
}

int SVCHlpClient::read (Guid &aGuid)
{
    Utf8Str guidStr;
    int vrc = read (guidStr);
    if (RT_SUCCESS(vrc))
        aGuid = Guid (guidStr.c_str());
    return vrc;
}

////////////////////////////////////////////////////////////////////////////////

SVCHlpServer::SVCHlpServer ()
{
}

int SVCHlpServer::run()
{
    int vrc = VINF_SUCCESS;
    SVCHlpMsg::Code msgCode = SVCHlpMsg::Null;

    do
    {
        vrc = read (msgCode);
        if (RT_FAILURE(vrc))
            return vrc;

        /* terminate request received */
        if (msgCode == SVCHlpMsg::Null)
            return VINF_SUCCESS;

        switch (msgCode)
        {
            case SVCHlpMsg::CreateHostOnlyNetworkInterface:
            case SVCHlpMsg::RemoveHostOnlyNetworkInterface:
            case SVCHlpMsg::EnableDynamicIpConfig:
            case SVCHlpMsg::EnableStaticIpConfig:
            case SVCHlpMsg::EnableStaticIpConfigV6:
            case SVCHlpMsg::DhcpRediscover:
            {
#ifdef VBOX_WITH_NETFLT
                vrc = netIfNetworkInterfaceHelperServer (this, msgCode);
#endif
                break;
            }
            default:
                AssertMsgFailedReturn ((
                    "Invalid message code %d (%08lX)\n", msgCode, msgCode),
                    VERR_GENERAL_FAILURE);
        }

        if (RT_FAILURE(vrc))
            return vrc;
    }
    while (1);

    /* we never get here */
    AssertFailed();
    return VERR_GENERAL_FAILURE;
}
