<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/ppapi_plugin/ppapi_webkitplatformsupport_impl.h"

#include <map>

#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/synchronization/lock.h"
#include "build/build_config.h"
#include "content/child/child_thread.h"
#include "content/common/child_process_messages.h"
#include "ppapi/proxy/plugin_globals.h"
#include "third_party/WebKit/public/platform/WebString.h"

#if defined(OS_WIN)
#include "third_party/WebKit/public/platform/win/WebSandboxSupport.h"
#elif defined(OS_MACOSX)
#include "third_party/WebKit/public/platform/mac/WebSandboxSupport.h"
#elif defined(OS_ANDROID)
#include "third_party/WebKit/public/platform/android/WebSandboxSupport.h"
#elif defined(OS_POSIX)
#include "content/common/child_process_sandbox_support_impl_linux.h"
#include "third_party/WebKit/public/platform/linux/WebFontFamily.h"
#include "third_party/WebKit/public/platform/linux/WebSandboxSupport.h"
#include "third_party/icu/source/common/unicode/utf16.h"
#endif

using blink::WebSandboxSupport;
using blink::WebString;
using blink::WebUChar;
using blink::WebUChar32;

typedef struct CGFont* CGFontRef;

namespace content {

class PpapiWebKitPlatformSupportImpl::SandboxSupport
    : public WebSandboxSupport {
 public:
  virtual ~SandboxSupport() {}

#if defined(OS_WIN)
  virtual bool ensureFontLoaded(HFONT);
#elif defined(OS_MACOSX)
  virtual bool loadFont(
      NSFont* srcFont, CGFontRef* out, uint32_t* fontID);
#elif defined(OS_ANDROID)
  // Empty class.
#elif defined(OS_POSIX)
  virtual void getFontFamilyForCharacter(
      WebUChar32 character,
      const char* preferred_locale,
      blink::WebFontFamily* family);
  virtual void getRenderStyleForStrike(
      const char* family, int sizeAndStyle, blink::WebFontRenderStyle* out);

 private:
  // WebKit likes to ask us for the correct font family to use for a set of
  // unicode code points. It needs this information frequently so we cache it
  // here.
  base::Lock unicode_font_families_mutex_;
  std::map<int32_t, blink::WebFontFamily> unicode_font_families_;
#endif
};

#if defined(OS_WIN)

bool PpapiWebKitPlatformSupportImpl::SandboxSupport::ensureFontLoaded(
    HFONT font) {
  LOGFONT logfont;
  GetObject(font, sizeof(LOGFONT), &logfont);

  // Use the proxy sender rather than going directly to the ChildThread since
  // the proxy browser sender will properly unlock during sync messages.
  return ppapi::proxy::PluginGlobals::Get()->GetBrowserSender()->Send(
      new ChildProcessHostMsg_PreCacheFont(logfont));
}

#elif defined(OS_MACOSX)

bool PpapiWebKitPlatformSupportImpl::SandboxSupport::loadFont(
    NSFont* src_font,
    CGFontRef* out,
    uint32_t* font_id) {
  // TODO(brettw) this should do the something similar to what
  // RendererWebKitClientImpl does and request that the browser load the font.
  // Note: need to unlock the proxy lock like ensureFontLoaded does.
  NOTIMPLEMENTED();
  return false;
}

#elif defined(OS_ANDROID)

// Empty class.

#elif defined(OS_POSIX)

void
PpapiWebKitPlatformSupportImpl::SandboxSupport::getFontFamilyForCharacter(
    WebUChar32 character,
    const char* preferred_locale,
    blink::WebFontFamily* family) {
  base::AutoLock lock(unicode_font_families_mutex_);
  const std::map<int32_t, blink::WebFontFamily>::const_iterator iter =
      unicode_font_families_.find(character);
  if (iter != unicode_font_families_.end()) {
    family->name = iter->second.name;
    family->isBold = iter->second.isBold;
    family->isItalic = iter->second.isItalic;
    return;
  }

  GetFontFamilyForCharacter(character, preferred_locale, family);
  unicode_font_families_.insert(std::make_pair(character, *family));
}

void PpapiWebKitPlatformSupportImpl::SandboxSupport::getRenderStyleForStrike(
    const char* family, int sizeAndStyle, blink::WebFontRenderStyle* out) {
  GetRenderStyleForStrike(family, sizeAndStyle, out);
}

#endif

PpapiWebKitPlatformSupportImpl::PpapiWebKitPlatformSupportImpl()
    : sandbox_support_(new PpapiWebKitPlatformSupportImpl::SandboxSupport()) {
}

PpapiWebKitPlatformSupportImpl::~PpapiWebKitPlatformSupportImpl() {
}

blink::WebClipboard* PpapiWebKitPlatformSupportImpl::clipboard() {
  NOTREACHED();
  return NULL;
}

blink::WebMimeRegistry* PpapiWebKitPlatformSupportImpl::mimeRegistry() {
  NOTREACHED();
  return NULL;
}

blink::WebFileUtilities* PpapiWebKitPlatformSupportImpl::fileUtilities() {
  NOTREACHED();
  return NULL;
}

blink::WebSandboxSupport* PpapiWebKitPlatformSupportImpl::sandboxSupport() {
  return sandbox_support_.get();
}

bool PpapiWebKitPlatformSupportImpl::sandboxEnabled() {
  return true;  // Assume PPAPI is always sandboxed.
}

unsigned long long PpapiWebKitPlatformSupportImpl::visitedLinkHash(
    const char* canonical_url,
    size_t length) {
  NOTREACHED();
  return 0;
}

bool PpapiWebKitPlatformSupportImpl::isLinkVisited(
    unsigned long long link_hash) {
  NOTREACHED();
  return false;
}

blink::WebMessagePortChannel*
PpapiWebKitPlatformSupportImpl::createMessagePortChannel() {
  NOTREACHED();
  return NULL;
}

void PpapiWebKitPlatformSupportImpl::setCookies(
    const blink::WebURL& url,
    const blink::WebURL& first_party_for_cookies,
    const blink::WebString& value) {
  NOTREACHED();
}

blink::WebString PpapiWebKitPlatformSupportImpl::cookies(
    const blink::WebURL& url,
    const blink::WebURL& first_party_for_cookies) {
  NOTREACHED();
  return blink::WebString();
}

blink::WebString PpapiWebKitPlatformSupportImpl::defaultLocale() {
  NOTREACHED();
  return blink::WebString();
}

blink::WebThemeEngine* PpapiWebKitPlatformSupportImpl::themeEngine() {
  NOTREACHED();
  return NULL;
}

blink::WebURLLoader* PpapiWebKitPlatformSupportImpl::createURLLoader() {
  NOTREACHED();
  return NULL;
}

blink::WebSocketStreamHandle*
    PpapiWebKitPlatformSupportImpl::createSocketStreamHandle() {
  NOTREACHED();
  return NULL;
}

void PpapiWebKitPlatformSupportImpl::getPluginList(bool refresh,
    blink::WebPluginListBuilder* builder) {
  NOTREACHED();
}

blink::WebData PpapiWebKitPlatformSupportImpl::loadResource(const char* name) {
  NOTREACHED();
  return blink::WebData();
}

blink::WebStorageNamespace*
PpapiWebKitPlatformSupportImpl::createLocalStorageNamespace() {
  NOTREACHED();
  return 0;
}

void PpapiWebKitPlatformSupportImpl::dispatchStorageEvent(
    const blink::WebString& key, const blink::WebString& old_value,
    const blink::WebString& new_value, const blink::WebString& origin,
    const blink::WebURL& url, bool is_local_storage) {
  NOTREACHED();
}

int PpapiWebKitPlatformSupportImpl::databaseDeleteFile(
    const blink::WebString& vfs_file_name, bool sync_dir) {
  NOTREACHED();
  return 0;
}

}  // namespace content
<commit_msg>PPAPI: Check that blink Font code is only called on the main thread<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/ppapi_plugin/ppapi_webkitplatformsupport_impl.h"

#include <map>

#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
#include "content/child/child_thread.h"
#include "content/common/child_process_messages.h"
#include "ppapi/proxy/plugin_globals.h"
#include "ppapi/shared_impl/proxy_lock.h"
#include "third_party/WebKit/public/platform/WebString.h"

#if defined(OS_WIN)
#include "third_party/WebKit/public/platform/win/WebSandboxSupport.h"
#elif defined(OS_MACOSX)
#include "third_party/WebKit/public/platform/mac/WebSandboxSupport.h"
#elif defined(OS_ANDROID)
#include "third_party/WebKit/public/platform/android/WebSandboxSupport.h"
#elif defined(OS_POSIX)
#include "content/common/child_process_sandbox_support_impl_linux.h"
#include "third_party/WebKit/public/platform/linux/WebFontFamily.h"
#include "third_party/WebKit/public/platform/linux/WebSandboxSupport.h"
#include "third_party/icu/source/common/unicode/utf16.h"
#endif

using blink::WebSandboxSupport;
using blink::WebString;
using blink::WebUChar;
using blink::WebUChar32;

typedef struct CGFont* CGFontRef;

namespace content {

class PpapiWebKitPlatformSupportImpl::SandboxSupport
    : public WebSandboxSupport {
 public:
  virtual ~SandboxSupport() {}

#if defined(OS_WIN)
  virtual bool ensureFontLoaded(HFONT);
#elif defined(OS_MACOSX)
  virtual bool loadFont(
      NSFont* srcFont, CGFontRef* out, uint32_t* fontID);
#elif defined(OS_ANDROID)
  // Empty class.
#elif defined(OS_POSIX)
  SandboxSupport();
  virtual void getFontFamilyForCharacter(
      WebUChar32 character,
      const char* preferred_locale,
      blink::WebFontFamily* family);
  virtual void getRenderStyleForStrike(
      const char* family, int sizeAndStyle, blink::WebFontRenderStyle* out);

 private:
  // WebKit likes to ask us for the correct font family to use for a set of
  // unicode code points. It needs this information frequently so we cache it
  // here.
  std::map<int32_t, blink::WebFontFamily> unicode_font_families_;
  // For debugging crbug.com/312965
  base::PlatformThreadId creation_thread_;
#endif
};

#if defined(OS_WIN)

bool PpapiWebKitPlatformSupportImpl::SandboxSupport::ensureFontLoaded(
    HFONT font) {
  LOGFONT logfont;
  GetObject(font, sizeof(LOGFONT), &logfont);

  // Use the proxy sender rather than going directly to the ChildThread since
  // the proxy browser sender will properly unlock during sync messages.
  return ppapi::proxy::PluginGlobals::Get()->GetBrowserSender()->Send(
      new ChildProcessHostMsg_PreCacheFont(logfont));
}

#elif defined(OS_MACOSX)

bool PpapiWebKitPlatformSupportImpl::SandboxSupport::loadFont(
    NSFont* src_font,
    CGFontRef* out,
    uint32_t* font_id) {
  // TODO(brettw) this should do the something similar to what
  // RendererWebKitClientImpl does and request that the browser load the font.
  // Note: need to unlock the proxy lock like ensureFontLoaded does.
  NOTIMPLEMENTED();
  return false;
}

#elif defined(OS_ANDROID)

// Empty class.

#elif defined(OS_POSIX)

PpapiWebKitPlatformSupportImpl::SandboxSupport::SandboxSupport()
    : creation_thread_(base::PlatformThread::CurrentId()) {
}

void
PpapiWebKitPlatformSupportImpl::SandboxSupport::getFontFamilyForCharacter(
    WebUChar32 character,
    const char* preferred_locale,
    blink::WebFontFamily* family) {
  ppapi::ProxyLock::AssertAcquired();
  // For debugging crbug.com/312965
  CHECK_EQ(creation_thread_, base::PlatformThread::CurrentId());
  const std::map<int32_t, blink::WebFontFamily>::const_iterator iter =
      unicode_font_families_.find(character);
  if (iter != unicode_font_families_.end()) {
    family->name = iter->second.name;
    family->isBold = iter->second.isBold;
    family->isItalic = iter->second.isItalic;
    return;
  }

  GetFontFamilyForCharacter(character, preferred_locale, family);
  unicode_font_families_.insert(std::make_pair(character, *family));
}

void PpapiWebKitPlatformSupportImpl::SandboxSupport::getRenderStyleForStrike(
    const char* family, int sizeAndStyle, blink::WebFontRenderStyle* out) {
  GetRenderStyleForStrike(family, sizeAndStyle, out);
}

#endif

PpapiWebKitPlatformSupportImpl::PpapiWebKitPlatformSupportImpl()
    : sandbox_support_(new PpapiWebKitPlatformSupportImpl::SandboxSupport()) {
}

PpapiWebKitPlatformSupportImpl::~PpapiWebKitPlatformSupportImpl() {
}

blink::WebClipboard* PpapiWebKitPlatformSupportImpl::clipboard() {
  NOTREACHED();
  return NULL;
}

blink::WebMimeRegistry* PpapiWebKitPlatformSupportImpl::mimeRegistry() {
  NOTREACHED();
  return NULL;
}

blink::WebFileUtilities* PpapiWebKitPlatformSupportImpl::fileUtilities() {
  NOTREACHED();
  return NULL;
}

blink::WebSandboxSupport* PpapiWebKitPlatformSupportImpl::sandboxSupport() {
  return sandbox_support_.get();
}

bool PpapiWebKitPlatformSupportImpl::sandboxEnabled() {
  return true;  // Assume PPAPI is always sandboxed.
}

unsigned long long PpapiWebKitPlatformSupportImpl::visitedLinkHash(
    const char* canonical_url,
    size_t length) {
  NOTREACHED();
  return 0;
}

bool PpapiWebKitPlatformSupportImpl::isLinkVisited(
    unsigned long long link_hash) {
  NOTREACHED();
  return false;
}

blink::WebMessagePortChannel*
PpapiWebKitPlatformSupportImpl::createMessagePortChannel() {
  NOTREACHED();
  return NULL;
}

void PpapiWebKitPlatformSupportImpl::setCookies(
    const blink::WebURL& url,
    const blink::WebURL& first_party_for_cookies,
    const blink::WebString& value) {
  NOTREACHED();
}

blink::WebString PpapiWebKitPlatformSupportImpl::cookies(
    const blink::WebURL& url,
    const blink::WebURL& first_party_for_cookies) {
  NOTREACHED();
  return blink::WebString();
}

blink::WebString PpapiWebKitPlatformSupportImpl::defaultLocale() {
  NOTREACHED();
  return blink::WebString();
}

blink::WebThemeEngine* PpapiWebKitPlatformSupportImpl::themeEngine() {
  NOTREACHED();
  return NULL;
}

blink::WebURLLoader* PpapiWebKitPlatformSupportImpl::createURLLoader() {
  NOTREACHED();
  return NULL;
}

blink::WebSocketStreamHandle*
    PpapiWebKitPlatformSupportImpl::createSocketStreamHandle() {
  NOTREACHED();
  return NULL;
}

void PpapiWebKitPlatformSupportImpl::getPluginList(bool refresh,
    blink::WebPluginListBuilder* builder) {
  NOTREACHED();
}

blink::WebData PpapiWebKitPlatformSupportImpl::loadResource(const char* name) {
  NOTREACHED();
  return blink::WebData();
}

blink::WebStorageNamespace*
PpapiWebKitPlatformSupportImpl::createLocalStorageNamespace() {
  NOTREACHED();
  return 0;
}

void PpapiWebKitPlatformSupportImpl::dispatchStorageEvent(
    const blink::WebString& key, const blink::WebString& old_value,
    const blink::WebString& new_value, const blink::WebString& origin,
    const blink::WebURL& url, bool is_local_storage) {
  NOTREACHED();
}

int PpapiWebKitPlatformSupportImpl::databaseDeleteFile(
    const blink::WebString& vfs_file_name, bool sync_dir) {
  NOTREACHED();
  return 0;
}

}  // namespace content
<|endoftext|>


<commit_before>#include "tests.h"
#include "interpreter.h"
#include "shell.h"
#include "../elpa/runtime.h"

using namespace elpa;

namespace kcon {

template<class Env>
struct Tester
{
    typedef typename Env::Allocator Allocator;
    typedef typename Env::elpa_ostream elpa_ostream;
    typedef typename Env::elpa_istream elpa_istream;
    typedef typename elpa_istream::Defns Defns;
    typedef typename elpa_istream::Readers Readers;
    typedef typename elpa_istream::Macros Macros;
    typedef typename Env::Interpreter Interpreter;
    typedef typename Shell<Env>::ShellManager ShellManager;

    typedef typename Env::Scheme::elem_t elem_t;

    template<class T>
    using auto_root_ref = typename Allocator::template auto_root_ref<T>;

    static auto parse( Allocator& allocator, const std::string& in, const Defns& defns, const Readers& readers, const Macros& macros ) -> auto_root_ref<elem_t>
    {
        std::istringstream iss( in );
        elem_t elem;
        elpa_istream( iss, allocator, defns, readers, macros ) >> elem;
        return auto_root_ref<elem_t>( allocator, elem );
    }

    static auto parse( Allocator& allocator, const std::string& in ) -> auto_root_ref<elem_t>
    {
    	Defns defns( allocator );
    	Readers readers = ShellManager::readers();
    	Macros macros = ShellManager::macros();
    	return parse( allocator, in, defns, readers, macros );
    }

    static auto print( elem_t e ) -> std::string
    {
        std::ostringstream oss;
        elpa_ostream( oss ) << e;
        return oss.str();
    }

    static void test_parse()
    {TEST
        Allocator a( 1024 );
        test( print( parse( a, "#1000" ) ) == "[232 3 0 0]", "Incorrect parse of #" );

        try
        {
            parse( a, "#40000000000" );
            fail( "Parsed integer larger that 32bits with #" );
        }
        catch( Error<Syntax> ) { pass(); }

        auto test_parse = [&]( const std::string& in, const std::string& out )
        {
            elem_t elem = parse( a, in );

            std::string found = print( elem );

            test( found == out, "Parse failed for: '" + in + "'\nExpected: '" + out + "'\nFound:    '" + found + "'" );
        };

        test_parse( "[1 2 < 3]", "[2 1 3]" );
        test_parse( "[1 2 3 <]", "[3 2 1]" );
        test_parse( "[1 2 3 < 4 5]", "[3 2 1 4 5]" );
        test_parse( "[[1 1] [2 2] [3 3] <]", "[[3 3] [2 2] 1 1]" );
        test_parse( "1 <", "1" );
        test_parse( "[[1 2 < 3] 2 [0 < 1] [10 20 <] <]", "[[20 10] [0 1] 2 2 1 3]" );

        test_parse( "1'", "[0 1]" );
    }

    static void test_interpreter()
    {TEST
        Interpreter interpreter( 1024 );

        auto test_op = [&]( char op, const std::string& in, const std::string& out )
        {
            elem_t elem = parse( interpreter.allocator(), in );

            std::string found = print( interpreter.process_operator( op, elem ) );

            test( found == out, "Op "s + op + " failed for: '" + in + "'\nExpected: '" + out + "'\nFound:    '" + found + "'" );
        };

        auto test_op_illegal = [&]( char op, const std::string& in, const std::string& msg )
        {
            elem_t elem = parse( interpreter.allocator(), in );

            try
            {
                std::string found = print( interpreter.process_operator( op, elem ) );
                fail( "Op "s + op + " failed for: '" + in + "'\nExpected error: '" + msg + "'\nFound:    '" + found + "'" );
            }
            catch( const Error<IllegalOp>& e )
            {
                test( e.message() == msg, "Op "s + op + " failed for: '" + in + "'\nExpected error: '" + msg + "'\nFound:          '" + e.message() + "'" );
                pass();
            }
        };

        test_op_illegal( '!', "21", "Illegal byte argument for !" );
        test_op( '!', "[0 21]", "[0 21]" );
    }

    static void run_tests()
    {
        std::cout << "TEST: " << TYPENAME( Env );

        test_parse();
        test_interpreter();

        std::cout << "\n\n";
    }
};

void run_tests()
{
    Tester< Env< Debug, SimpleScheme, TestAllocator, KConInterpreter > >::run_tests();
    Tester< Env< Debug, SimpleScheme, SimpleAllocator, KConInterpreter > >::run_tests();
}

}	//namespace<commit_msg>Adding quote test<commit_after>#include "tests.h"
#include "interpreter.h"
#include "shell.h"
#include "../elpa/runtime.h"

using namespace elpa;

namespace kcon {

template<class Env>
struct Tester
{
    typedef typename Env::Allocator Allocator;
    typedef typename Env::elpa_ostream elpa_ostream;
    typedef typename Env::elpa_istream elpa_istream;
    typedef typename elpa_istream::Defns Defns;
    typedef typename elpa_istream::Readers Readers;
    typedef typename elpa_istream::Macros Macros;
    typedef typename Env::Interpreter Interpreter;
    typedef typename Shell<Env>::ShellManager ShellManager;

    typedef typename Env::Scheme::elem_t elem_t;

    template<class T>
    using auto_root_ref = typename Allocator::template auto_root_ref<T>;

    static auto parse( Allocator& allocator, const std::string& in, const Defns& defns, const Readers& readers, const Macros& macros ) -> auto_root_ref<elem_t>
    {
        std::istringstream iss( in );
        elem_t elem;
        elpa_istream( iss, allocator, defns, readers, macros ) >> elem;
        return auto_root_ref<elem_t>( allocator, elem );
    }

    static auto parse( Allocator& allocator, const std::string& in ) -> auto_root_ref<elem_t>
    {
    	Defns defns( allocator );
    	Readers readers = ShellManager::readers();
    	Macros macros = ShellManager::macros();
    	return parse( allocator, in, defns, readers, macros );
    }

    static auto print( elem_t e ) -> std::string
    {
        std::ostringstream oss;
        elpa_ostream( oss ) << e;
        return oss.str();
    }

    static void test_parse()
    {TEST
        Allocator a( 1024 );
        test( print( parse( a, "#1000" ) ) == "[232 3 0 0]", "Incorrect parse of #" );

        try
        {
            parse( a, "#40000000000" );
            fail( "Parsed integer larger that 32bits with #" );
        }
        catch( Error<Syntax> ) { pass(); }

        auto test_parse = [&]( const std::string& in, const std::string& out )
        {
            elem_t elem = parse( a, in );

            std::string found = print( elem );

            test( found == out, "Parse failed for: '" + in + "'\nExpected: '" + out + "'\nFound:    '" + found + "'" );
        };

        test_parse( "[1 2 < 3]", "[2 1 3]" );
        test_parse( "[1 2 3 <]", "[3 2 1]" );
        test_parse( "[1 2 3 < 4 5]", "[3 2 1 4 5]" );
        test_parse( "[[1 1] [2 2] [3 3] <]", "[[3 3] [2 2] 1 1]" );
        test_parse( "1 <", "1" );
        test_parse( "[[1 2 < 3] 2 [0 < 1] [10 20 <] <]", "[[20 10] [0 1] 2 2 1 3]" );

        test_parse( "[1'' 9]", "[[0 0 1] 9]" );
    }

    static void test_interpreter()
    {TEST
        Interpreter interpreter( 1024 );

        auto test_op = [&]( char op, const std::string& in, const std::string& out )
        {
            elem_t elem = parse( interpreter.allocator(), in );

            std::string found = print( interpreter.process_operator( op, elem ) );

            test( found == out, "Op "s + op + " failed for: '" + in + "'\nExpected: '" + out + "'\nFound:    '" + found + "'" );
        };

        auto test_op_illegal = [&]( char op, const std::string& in, const std::string& msg )
        {
            elem_t elem = parse( interpreter.allocator(), in );

            try
            {
                std::string found = print( interpreter.process_operator( op, elem ) );
                fail( "Op "s + op + " failed for: '" + in + "'\nExpected error: '" + msg + "'\nFound:    '" + found + "'" );
            }
            catch( const Error<IllegalOp>& e )
            {
                test( e.message() == msg, "Op "s + op + " failed for: '" + in + "'\nExpected error: '" + msg + "'\nFound:          '" + e.message() + "'" );
                pass();
            }
        };

        test_op_illegal( '!', "21", "Illegal byte argument for !" );
        test_op( '!', "[0 21]", "[0 21]" );
    }

    static void run_tests()
    {
        std::cout << "TEST: " << TYPENAME( Env );

        test_parse();
        test_interpreter();

        std::cout << "\n\n";
    }
};

void run_tests()
{
    Tester< Env< Debug, SimpleScheme, TestAllocator, KConInterpreter > >::run_tests();
    Tester< Env< Debug, SimpleScheme, SimpleAllocator, KConInterpreter > >::run_tests();
}

}	//namespace<|endoftext|>


<commit_before>//*****************************************************************************
// Shared message example
//*****************************************************************************

#include "etl/shared_message.h"
#include "etl/message.h"
#include "etl/reference_counted_message_pool.h"
#include "etl/message_router.h"
#include "etl/message_bus.h"
#include "etl/fixed_sized_memory_block_allocator.h"
#include "etl/queue.h"

#include <iostream>
#include <atomic>
#include <string>
#include <mutex>

constexpr etl::message_router_id_t RouterId1 = 1U;
constexpr etl::message_router_id_t RouterId2 = 2U;

//*****************************************************************************
// Message1
//*****************************************************************************
struct Message1 : public etl::message<1>
{
  Message1(std::string s_)
    : s(s_)
  {

  }

  std::string s;
};

//*****************************************************************************
// Message2
//*****************************************************************************
struct Message2 : public etl::message<2>
{
  Message2(std::string s_)
    : s(s_)
  {

  }

  std::string s;
  char data[100];
};

//*****************************************************************************
// Message3
//*****************************************************************************
struct Message3 : public etl::message<3>
{
  Message3(std::string s_)
    : s(s_)
  {

  }

  std::string s;
};

//*****************************************************************************
// Prints the shared message
//*****************************************************************************
void Print(const std::string& prefix, etl::shared_message sm)
{
  std::cout << prefix << " : Message Id = " << int(sm.get_message().get_message_id()) << "\n";
}

//*****************************************************************************
// This router accepts Message1, Message2 and Message3 types.
// If a shared message it received, it will be processed immediately.
//*****************************************************************************
class MessageRouter1 : public etl::message_router<MessageRouter1, Message1, Message2, Message3>
{
public:

  //****************************************
  MessageRouter1()
    : message_router(RouterId1)
  {
  }

  //****************************************
  void on_receive(etl::imessage_router& source, const Message1& msg)
  {
    std::cout << "MessageRouter1 : on_receive Message1 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive(etl::imessage_router& source, const Message2& msg)
  {
    std::cout << "MessageRouter1 : on_receive Message2 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive(etl::imessage_router& source, const Message3& msg)
  {
    std::cout << "MessageRouter1 : on_receive Message3 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive_unknown(etl::imessage_router& source, const etl::imessage& msg)
  {
    std::cout << "MessageRouter1 : on_receive Unknown\n";
  }
};

//*****************************************************************************
// This router accepts Message1, Message2 and Message3 types.
// If a shared message it received it will queue them.
// The messages will be processed when process_queue() is called.
//*****************************************************************************
class MessageRouter2 : public etl::message_router<MessageRouter2, Message1, Message2, Message3>
{
public:

  using base_t = etl::message_router<MessageRouter2, Message1, Message2, Message3>;

  //****************************************
  MessageRouter2()
    : message_router(RouterId2)
  {
  }

  using base_t::receive;

  //****************************************
  // Overridden receive.
  // Puts the shared messages into a queue.
  void receive(etl::imessage_router& source, etl::shared_message shared_msg) override
  {
    if (!queue.full())
    {
      Print("MessageRouter2 : Queueing shared message", shared_msg);

      queue.push(QueuedData{ &source, shared_msg });
    }
  }

  //****************************************
  // Processes the queued shared messages.
  void process_queue()
  {
    while (!queue.empty())
    {
      // Get the shared message from the queue.
      QueuedData data = queue.front();

      Print("MessageRouter2 : Process queued shared message", data.shared_msg);

      // Send it to the base implementation for routing.
      base_t::receive(*data.source, data.shared_msg);

      queue.pop();
    }
  }

  //****************************************
  void on_receive(etl::imessage_router& source, const Message1& msg)
  {
    std::cout << "MessageRouter2 : on_receive Message1 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive(etl::imessage_router& source, const Message2& msg)
  {
    std::cout << "MessageRouter2 : on_receive Message2 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive(etl::imessage_router& source, const Message3& msg)
  {
    std::cout << "MessageRouter2 : on_receive Message3 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive_unknown(etl::imessage_router& source, const etl::imessage& msg)
  {
    std::cout << "MessageRouter2 : on_receive Unknown\n";
  }

private:

  struct QueuedData
  {
    etl::imessage_router* source;
    etl::shared_message   shared_msg;
  };

  etl::queue<QueuedData, 10> queue;
};

//*****************************************************************************
// A message bus that can accomodate two subscribers.
//*****************************************************************************
struct Bus : public etl::message_bus<2U>
{
};

//*****************************************************************************
// Define the routers and bus.
//*****************************************************************************
MessageRouter1 router1;
MessageRouter2 router2;
Bus bus;

//*****************************************************************************
// The thread safe message pool. Uses atomic uint32_t for counting.
class MessagePool : public etl::reference_counted_message_pool<std::atomic_int32_t>
{
public:

  MessagePool(etl::imemory_block_allocator& allocator)
    : reference_counted_message_pool(allocator)
  {
  }

  // Called before the memory block allocator is accessed.
  void lock() override
  {
    mut.lock();
  }

  // Called after the memory block allocator has been accessed.
  void unlock() override
  {
    mut.unlock();
  }

private:

  std::mutex mut;
};

//*****************************************************************************
// The memory block allocator that supplies the pool with memory
// to store reference counted messages in.

// The reference counted message parameters type for the messages we will use.
using message_parameters_small = MessagePool::pool_message_parameters<Message1, Message3>;
using message_parameters_large = MessagePool::pool_message_parameters<Message2>;

constexpr size_t max_size_small      = message_parameters_small::max_size;
constexpr size_t max_alignment_small = message_parameters_small::max_alignment;

constexpr size_t max_size_large      = message_parameters_large::max_size;
constexpr size_t max_alignment_large = message_parameters_large::max_alignment;

// A fixed memory block allocator for 4 items, using the parameters from the smaller messages.
etl::fixed_sized_memory_block_allocator<max_size_small, max_alignment_small, 4U> memory_allocator;

// A fixed memory block allocator for 4 items, using the parameters from the larger message.
etl::fixed_sized_memory_block_allocator<max_size_large, max_alignment_large, 4U> memory_allocator_successor;

//*****************************************************************************
// The pool that supplies reference counted messages.
// Uses memory_allocator as its allocator.
//*****************************************************************************
MessagePool message_pool(memory_allocator);

//*****************************************************************************
// A statically allocated reference counted message that is never allocated or released by a pool.
// Contains a copy of Message3("Three").
//*****************************************************************************
etl::persistent_message<Message3> pm3(Message3("Three"));

//*****************************************************************************
int main()
{
  // If memory_allocator can't allocate, then try memory_allocator_successor.
  memory_allocator.set_successor(memory_allocator_successor);

  Message1 m1("One");
  Message2 m2("Two");

  etl::shared_message sm1(message_pool, m1); // Created a shared message by allocating a reference counted message from message_pool containing a copy of m1.
  etl::shared_message sm2(message_pool, m2); // Created a shared message by allocating a reference counted message from message_pool containing a copy of m2.
  etl::shared_message sm3(pm3);              // Created a shared message from a statically allocated persistent message.

  bus.subscribe(router1); // Subscribe router1 to the bus.
  bus.subscribe(router2); // Subscribe router2 to the bus.

  bus.receive(sm1); // Send sm1 to the bus for distribution to the routers.
  bus.receive(sm2); // Send sm2 to the bus for distribution to the routers.
  bus.receive(sm3); // Send sm3 to the bus for distribution to the routers.

  router2.process_queue(); // Allow router2 to process its queued messages.
}
<commit_msg>Updated example for shared_message<commit_after>//*****************************************************************************
// Shared message example
//*****************************************************************************

#include "etl/shared_message.h"
#include "etl/message.h"
#include "etl/reference_counted_message_pool.h"
#include "etl/message_router.h"
#include "etl/message_bus.h"
#include "etl/fixed_sized_memory_block_allocator.h"
#include "etl/queue.h"

#include <iostream>
#include <atomic>
#include <string>
#include <mutex>

constexpr etl::message_router_id_t RouterId1 = 1U;
constexpr etl::message_router_id_t RouterId2 = 2U;

//*****************************************************************************
// Message1
//*****************************************************************************
struct Message1 : public etl::message<1>
{
  Message1(std::string s_)
    : s(s_)
  {

  }

  std::string s;
};

//*****************************************************************************
// Message2
//*****************************************************************************
struct Message2 : public etl::message<2>
{
  Message2(std::string s_)
    : s(s_)
  {

  }

  std::string s;
  char data[100];
};

//*****************************************************************************
// Message3
//*****************************************************************************
struct Message3 : public etl::message<3>
{
  Message3(std::string s_)
    : s(s_)
  {

  }

  std::string s;
};

//*****************************************************************************
// Prints the shared message
//*****************************************************************************
void Print(const std::string& prefix, etl::shared_message sm)
{
  std::cout << prefix << " : Message Id = " << int(sm.get_message().get_message_id()) << "\n";
}

//*****************************************************************************
// This router accepts Message1, Message2 and Message3 types.
// If a shared message it received, it will be processed immediately.
//*****************************************************************************
class MessageRouter1 : public etl::message_router<MessageRouter1, Message1, Message2, Message3>
{
public:

  //****************************************
  MessageRouter1()
    : message_router(RouterId1)
  {
  }

  //****************************************
  void on_receive(const Message1& msg)
  {
    std::cout << "MessageRouter1 : on_receive Message1 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive(const Message2& msg)
  {
    std::cout << "MessageRouter1 : on_receive Message2 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive(const Message3& msg)
  {
    std::cout << "MessageRouter1 : on_receive Message3 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive_unknown(const etl::imessage& msg)
  {
    std::cout << "MessageRouter1 : on_receive Unknown\n";
  }
};

//*****************************************************************************
// This router accepts Message1, Message2 and Message3 types.
// If a shared message it received it will queue them.
// The messages will be processed when process_queue() is called.
//*****************************************************************************
class MessageRouter2 : public etl::message_router<MessageRouter2, Message1, Message2, Message3>
{
public:

  using base_t = etl::message_router<MessageRouter2, Message1, Message2, Message3>;

  //****************************************
  MessageRouter2()
    : message_router(RouterId2)
  {
  }

  using base_t::receive;

  //****************************************
  // Overridden receive.
  // Puts the shared messages into a queue.
  void receive(etl::shared_message shared_msg) override
  {
    if (!queue.full())
    {
      Print("MessageRouter2 : Queueing shared message", shared_msg);

      queue.push(shared_msg);
    }
  }

  //****************************************
  // Processes the queued shared messages.
  void process_queue()
  {
    while (!queue.empty())
    {
      // Get the shared message from the queue.
      etl::shared_message shared_msg = queue.front();

      Print("MessageRouter2 : Process queued shared message", shared_msg);

      // Send it to the base implementation for routing.
      base_t::receive(shared_msg);

      queue.pop();
    }
  }

  //****************************************
  void on_receive(const Message1& msg)
  {
    std::cout << "MessageRouter2 : on_receive Message1 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive(const Message2& msg)
  {
    std::cout << "MessageRouter2 : on_receive Message2 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive(const Message3& msg)
  {
    std::cout << "MessageRouter2 : on_receive Message3 : " << msg.s << "\n";
  }

  //****************************************
  void on_receive_unknown(const etl::imessage& msg)
  {
    std::cout << "MessageRouter2 : on_receive Unknown\n";
  }

private:

  etl::queue<etl::shared_message, 10> queue;
};

//*****************************************************************************
// A message bus that can accomodate two subscribers.
//*****************************************************************************
struct Bus : public etl::message_bus<2U>
{
};

//*****************************************************************************
// Define the routers and bus.
//*****************************************************************************
MessageRouter1 router1;
MessageRouter2 router2;
Bus bus;

//*****************************************************************************
// The thread safe message pool. Uses atomic uint32_t for counting.
class MessagePool : public etl::reference_counted_message_pool<std::atomic_int32_t>
{
public:

  MessagePool(etl::imemory_block_allocator& allocator)
    : reference_counted_message_pool(allocator)
  {
  }

  // Called before the memory block allocator is accessed.
  void lock() override
  {
    mut.lock();
  }

  // Called after the memory block allocator has been accessed.
  void unlock() override
  {
    mut.unlock();
  }

private:

  std::mutex mut;
};

//*****************************************************************************
// The memory block allocator that supplies the pool with memory
// to store reference counted messages in.

// The reference counted message parameters type for the messages we will use.
using message_parameters_small = MessagePool::pool_message_parameters<Message1, Message3>;
using message_parameters_large = MessagePool::pool_message_parameters<Message2>;

constexpr size_t max_size_small      = message_parameters_small::max_size;
constexpr size_t max_alignment_small = message_parameters_small::max_alignment;

constexpr size_t max_size_large      = message_parameters_large::max_size;
constexpr size_t max_alignment_large = message_parameters_large::max_alignment;

// A fixed memory block allocator for 4 items, using the parameters from the smaller messages.
etl::fixed_sized_memory_block_allocator<max_size_small, max_alignment_small, 4U> memory_allocator;

// A fixed memory block allocator for 4 items, using the parameters from the larger message.
etl::fixed_sized_memory_block_allocator<max_size_large, max_alignment_large, 4U> memory_allocator_successor;

//*****************************************************************************
// The pool that supplies reference counted messages.
// Uses memory_allocator as its allocator.
//*****************************************************************************
MessagePool message_pool(memory_allocator);

//*****************************************************************************
// A statically allocated reference counted message that is never allocated or released by a pool.
// Contains a copy of Message3("Three").
//*****************************************************************************
etl::persistent_message<Message3> pm3(Message3("Three"));

//*****************************************************************************
int main()
{
  // If memory_allocator can't allocate, then try memory_allocator_successor.
  memory_allocator.set_successor(memory_allocator_successor);

  Message1 m1("One");
  Message2 m2("Two");

  etl::shared_message sm1(message_pool, m1); // Created a shared message by allocating a reference counted message from message_pool containing a copy of m1.
  etl::shared_message sm2(message_pool, m2); // Created a shared message by allocating a reference counted message from message_pool containing a copy of m2.
  etl::shared_message sm3(pm3);              // Created a shared message from a statically allocated persistent message.

  bus.subscribe(router1); // Subscribe router1 to the bus.
  bus.subscribe(router2); // Subscribe router2 to the bus.

  bus.receive(sm1); // Send sm1 to the bus for distribution to the routers.
  bus.receive(sm2); // Send sm2 to the bus for distribution to the routers.
  bus.receive(sm3); // Send sm3 to the bus for distribution to the routers.

  router2.process_queue(); // Allow router2 to process its queued messages.
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "mmcore/utility/LuaHostService.h"
#include <combaseapi.h>
#include "mmcore/CoreInstance.h"
#include "vislib/math/mathfunctions.h"

using namespace megamol;
using megamol::core::AbstractNamedObject;
using megamol::core::AbstractNamedObjectContainer;

unsigned int megamol::core::utility::LuaHostService::ID = 0;

megamol::core::utility::LuaHostService::LuaHostService(core::CoreInstance& core)
    : AbstractService(core), serverThread(), serverRunning(false), address("tcp://*:33333") {
    // Intentionally empty
}

megamol::core::utility::LuaHostService::~LuaHostService() { assert(!this->IsEnabled()); }

bool megamol::core::utility::LuaHostService::Initalize(bool& autoEnable) {
    using vislib::sys::Log;
    context = ZMQContextUser::Instance();

    // fetch configuration
    auto& cfg = GetCoreInstance().Configuration();

    if (cfg.IsConfigValueSet("LRHostAddress")) {
        mmcValueType t;
        const void* d = cfg.GetValue(MMC_CFGID_VARIABLE, "LRHostAddress", &t);
        switch (t) {
        case MMC_TYPE_CSTR:
            address = static_cast<const char*>(d);
            Log::DefaultLog.WriteInfo("Set LRHostAddress = \"%s\"", address.c_str());
            break;
        case MMC_TYPE_WSTR:
            address = vislib::StringA(static_cast<const wchar_t*>(d));
            Log::DefaultLog.WriteInfo("Set LRHostAddress = \"%s\"", address.c_str());
            break;
        default:
            Log::DefaultLog.WriteWarn(
                "Unable to set LRHostAddress: expected string, but found type %d", static_cast<int>(t));
            break;
        }
    } else {
        Log::DefaultLog.WriteInfo("Default LRHostAddress = \"%s\"", address.c_str());
    }

    autoEnable = true; // default behavior

    if (cfg.IsConfigValueSet("LRHostEnable")) {
        mmcValueType t;
        const void* d = cfg.GetValue(MMC_CFGID_VARIABLE, "LRHostEnable", &t);
        bool silent = false;
        switch (t) {
        case MMC_TYPE_INT32:
            autoEnable = ((*static_cast<const int32_t*>(d)) != 0);
            break;
        case MMC_TYPE_UINT32:
            autoEnable = ((*static_cast<const uint32_t*>(d)) != 0);
            break;
        case MMC_TYPE_INT64:
            autoEnable = ((*static_cast<const int64_t*>(d)) != 0);
            break;
        case MMC_TYPE_UINT64:
            autoEnable = ((*static_cast<const uint64_t*>(d)) != 0);
            break;
        case MMC_TYPE_BYTE:
            autoEnable = ((*static_cast<const uint8_t*>(d)) != 0);
            break;
        case MMC_TYPE_BOOL:
            autoEnable = *static_cast<const bool*>(d);
            break;
        case MMC_TYPE_FLOAT:
            autoEnable = !vislib::math::IsEqual(*static_cast<const float*>(d), 0.0f);
            break;
        case MMC_TYPE_CSTR:
            autoEnable = vislib::CharTraitsA::ParseBool(static_cast<const char*>(d));
            break;
        case MMC_TYPE_WSTR:
            autoEnable = vislib::CharTraitsW::ParseBool(static_cast<const wchar_t*>(d));
            break;
        default:
            Log::DefaultLog.WriteWarn(
                "Unable to set LRHostEnable: expected string, but found type %d", static_cast<int>(t));
            silent = true;
            break;
        }
        if (!silent) {
            Log::DefaultLog.WriteInfo("Set LRHostEnable = \"%s\"", autoEnable ? "true" : "false");
        }
    } else {
        Log::DefaultLog.WriteInfo("Default LRHostEnable = \"%s\"", autoEnable ? "true" : "false");
    }

    return true;
}

bool megamol::core::utility::LuaHostService::Deinitialize() {
    Disable();
    context.reset();
    return true;
}

void megamol::core::utility::LuaHostService::SetAddress(const std::string& ad) {
    if (serverRunning) {
        // restart server
        disableImpl();
        address = ad;
        enableImpl();
    } else {
        address = ad;
    }
}

bool megamol::core::utility::LuaHostService::enableImpl() {
    assert(serverRunning == false);
    serverThread = std::thread([&]() { this->serve(); });
    while (!serverRunning) std::this_thread::sleep_for(std::chrono::milliseconds(10));
    return true;
}

bool megamol::core::utility::LuaHostService::disableImpl() {
    serverRunning = false;
    if (serverThread.joinable()) serverThread.join();
    for (auto& x : this->pairThreads) {
        if (x.joinable()) x.join();
    }
    return true;
}

void megamol::core::utility::LuaHostService::serve() {
    using vislib::sys::Log;

    zmq::socket_t socket(*context, ZMQ_REP);

    try {
        serverRunning = true;
        socket.bind(address);
        socket.setsockopt(ZMQ_RCVTIMEO, 100); // message receive time out 100ms

        Log::DefaultLog.WriteInfo("LRH Server socket opened on \"%s\"", address.c_str());

        while (serverRunning) {
            zmq::message_t request;
            while (serverRunning && !socket.recv(&request, ZMQ_DONTWAIT)) {
                // no messages available ATM
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
            if (!serverRunning) break;

            std::string request_str(reinterpret_cast<char*>(request.data()), request.size());
            std::string reply = makeAnswer(request_str);
            socket.send(reply.data(), reply.size());
        }

    } catch (std::exception& error) {
        Log::DefaultLog.WriteError("Error on LRH Server: %s", error.what());

    } catch (...) {
        Log::DefaultLog.WriteError("Error on LRH Server: unknown exception");
    }

    try {
        socket.close();
    } catch (...) {
    }
    Log::DefaultLog.WriteInfo("LRH Server socket closed");
}

void core::utility::LuaHostService::servePair() {
    using vislib::sys::Log;

    auto socket = zmq::socket_t(*context, zmq::socket_type::pair);
    socket.bind("tcp://*:0");
    size_t len = 1024;
    char* opts = new char[len];
    socket.getsockopt(ZMQ_LAST_ENDPOINT, opts, &len);
    std::string endp(opts);
    delete[] opts;
    const auto portPos = endp.find_last_of(":");
    const auto portStr = endp.substr(portPos + 1, -1);
    this->lastPairPort.store(std::atoi(portStr.c_str()));

    try {
        while (serverRunning) {
            zmq::message_t request;
            if (!socket.connected()) break;
            while (serverRunning && !socket.recv(&request, ZMQ_DONTWAIT)) {
                // no messages available ATM
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
            if (!serverRunning) break;

            std::string request_str(reinterpret_cast<char*>(request.data()), request.size());
            std::string reply = makePairAnswer(request_str);
            socket.send(reply.data(), reply.size());
        }

    } catch (std::exception& error) {
        Log::DefaultLog.WriteError("Error on LRH Pair Server: %s", error.what());

    } catch (...) {
        Log::DefaultLog.WriteError("Error on LRH Pair Server: unknown exception");
    }

    try {
        socket.close();
    } catch (...) {
    }
    Log::DefaultLog.WriteInfo("LRH Server socket closed");
}

std::string megamol::core::utility::LuaHostService::makeAnswer(const std::string& req) {

    if (req.empty()) return std::string("Null Command.");

    int port;
    this->lastPairPort.store(0);

    this->pairThreads.emplace_back([&]() { this->servePair(); });

    while ((port = this->lastPairPort.load()) == 0 && serverRunning) {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

    vislib::sys::Log::DefaultLog.WriteInfo("LRH: generated PAIR socket on port %i", port);
    return std::to_string(port);
}

std::string megamol::core::utility::LuaHostService::makePairAnswer(const std::string& req) const {
    if (req.empty()) return std::string("Null Command.");

    std::string result;
    int ok = this->GetCoreInstance().GetLuaState()->RunString(req, result);
    if (ok) {
        // vislib::sys::Log::DefaultLog.WriteInfo("Lua execution is OK and returned '%s'", result.c_str());
    } else {
        vislib::sys::Log::DefaultLog.WriteError("Lua execution is NOT OK and returned '%s'", result.c_str());
        result = "Error: " + result;
    }
    return result;
}<commit_msg>you can make the LRH annoyingly verbose if needed<commit_after>#include "stdafx.h"
#include "mmcore/utility/LuaHostService.h"
#include <combaseapi.h>
#include "mmcore/CoreInstance.h"
#include "vislib/math/mathfunctions.h"

//#define LRH_ANNOYING_DETAILS

using namespace megamol;
using megamol::core::AbstractNamedObject;
using megamol::core::AbstractNamedObjectContainer;

unsigned int megamol::core::utility::LuaHostService::ID = 0;

megamol::core::utility::LuaHostService::LuaHostService(core::CoreInstance& core)
    : AbstractService(core), serverThread(), serverRunning(false), address("tcp://*:33333") {
    // Intentionally empty
}

megamol::core::utility::LuaHostService::~LuaHostService() { assert(!this->IsEnabled()); }

bool megamol::core::utility::LuaHostService::Initalize(bool& autoEnable) {
    using vislib::sys::Log;
    context = ZMQContextUser::Instance();

    // fetch configuration
    auto& cfg = GetCoreInstance().Configuration();

    if (cfg.IsConfigValueSet("LRHostAddress")) {
        mmcValueType t;
        const void* d = cfg.GetValue(MMC_CFGID_VARIABLE, "LRHostAddress", &t);
        switch (t) {
        case MMC_TYPE_CSTR:
            address = static_cast<const char*>(d);
            Log::DefaultLog.WriteInfo("Set LRHostAddress = \"%s\"", address.c_str());
            break;
        case MMC_TYPE_WSTR:
            address = vislib::StringA(static_cast<const wchar_t*>(d));
            Log::DefaultLog.WriteInfo("Set LRHostAddress = \"%s\"", address.c_str());
            break;
        default:
            Log::DefaultLog.WriteWarn(
                "Unable to set LRHostAddress: expected string, but found type %d", static_cast<int>(t));
            break;
        }
    } else {
        Log::DefaultLog.WriteInfo("Default LRHostAddress = \"%s\"", address.c_str());
    }

    autoEnable = true; // default behavior

    if (cfg.IsConfigValueSet("LRHostEnable")) {
        mmcValueType t;
        const void* d = cfg.GetValue(MMC_CFGID_VARIABLE, "LRHostEnable", &t);
        bool silent = false;
        switch (t) {
        case MMC_TYPE_INT32:
            autoEnable = ((*static_cast<const int32_t*>(d)) != 0);
            break;
        case MMC_TYPE_UINT32:
            autoEnable = ((*static_cast<const uint32_t*>(d)) != 0);
            break;
        case MMC_TYPE_INT64:
            autoEnable = ((*static_cast<const int64_t*>(d)) != 0);
            break;
        case MMC_TYPE_UINT64:
            autoEnable = ((*static_cast<const uint64_t*>(d)) != 0);
            break;
        case MMC_TYPE_BYTE:
            autoEnable = ((*static_cast<const uint8_t*>(d)) != 0);
            break;
        case MMC_TYPE_BOOL:
            autoEnable = *static_cast<const bool*>(d);
            break;
        case MMC_TYPE_FLOAT:
            autoEnable = !vislib::math::IsEqual(*static_cast<const float*>(d), 0.0f);
            break;
        case MMC_TYPE_CSTR:
            autoEnable = vislib::CharTraitsA::ParseBool(static_cast<const char*>(d));
            break;
        case MMC_TYPE_WSTR:
            autoEnable = vislib::CharTraitsW::ParseBool(static_cast<const wchar_t*>(d));
            break;
        default:
            Log::DefaultLog.WriteWarn(
                "Unable to set LRHostEnable: expected string, but found type %d", static_cast<int>(t));
            silent = true;
            break;
        }
        if (!silent) {
            Log::DefaultLog.WriteInfo("Set LRHostEnable = \"%s\"", autoEnable ? "true" : "false");
        }
    } else {
        Log::DefaultLog.WriteInfo("Default LRHostEnable = \"%s\"", autoEnable ? "true" : "false");
    }

    return true;
}

bool megamol::core::utility::LuaHostService::Deinitialize() {
    Disable();
    context.reset();
    return true;
}

void megamol::core::utility::LuaHostService::SetAddress(const std::string& ad) {
    if (serverRunning) {
        // restart server
        disableImpl();
        address = ad;
        enableImpl();
    } else {
        address = ad;
    }
}

bool megamol::core::utility::LuaHostService::enableImpl() {
    assert(serverRunning == false);
    serverThread = std::thread([&]() { this->serve(); });
    while (!serverRunning) std::this_thread::sleep_for(std::chrono::milliseconds(10));
    return true;
}

bool megamol::core::utility::LuaHostService::disableImpl() {
    serverRunning = false;
    if (serverThread.joinable()) serverThread.join();
    for (auto& x : this->pairThreads) {
        if (x.joinable()) x.join();
    }
    return true;
}

void megamol::core::utility::LuaHostService::serve() {
    using vislib::sys::Log;

    zmq::socket_t socket(*context, ZMQ_REP);

    try {
        serverRunning = true;
        socket.bind(address);
        socket.setsockopt(ZMQ_RCVTIMEO, 100); // message receive time out 100ms

        Log::DefaultLog.WriteInfo("LRH Server socket opened on \"%s\"", address.c_str());

        while (serverRunning) {
            zmq::message_t request;
            while (serverRunning && !socket.recv(&request, ZMQ_DONTWAIT)) {
                // no messages available ATM
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
            if (!serverRunning) break;

            std::string request_str(reinterpret_cast<char*>(request.data()), request.size());
            std::string reply = makeAnswer(request_str);
            socket.send(reply.data(), reply.size());
        }

    } catch (std::exception& error) {
        Log::DefaultLog.WriteError("Error on LRH Server: %s", error.what());

    } catch (...) {
        Log::DefaultLog.WriteError("Error on LRH Server: unknown exception");
    }

    try {
        socket.close();
    } catch (...) {
    }
    Log::DefaultLog.WriteInfo("LRH Server socket closed");
}

void core::utility::LuaHostService::servePair() {
    using vislib::sys::Log;

    auto socket = zmq::socket_t(*context, zmq::socket_type::pair);
    socket.bind("tcp://*:0");
    size_t len = 1024;
    char* opts = new char[len];
    socket.getsockopt(ZMQ_LAST_ENDPOINT, opts, &len);
    std::string endp(opts);
    delete[] opts;
    const auto portPos = endp.find_last_of(":");
    const auto portStr = endp.substr(portPos + 1, -1);
    this->lastPairPort.store(std::atoi(portStr.c_str()));

    try {
        while (serverRunning) {
            zmq::message_t request;
            if (!socket.connected()) break;
            while (serverRunning && !socket.recv(&request, ZMQ_DONTWAIT)) {
                // no messages available ATM
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
            if (!serverRunning) break;

            std::string request_str(reinterpret_cast<char*>(request.data()), request.size());
            std::string reply = makePairAnswer(request_str);
            const auto num_sent = socket.send(reply.data(), reply.size());
#ifdef LRH_ANNOYING_DETAILS
            if (num_sent == reply.size()) {
                vislib::sys::Log::DefaultLog.WriteInfo("LRH: sending looks OK");
            } else {
                vislib::sys::Log::DefaultLog.WriteError("LRH: send failed");
            }
#endif
        }

    } catch (std::exception& error) {
        Log::DefaultLog.WriteError("Error on LRH Pair Server: %s", error.what());

    } catch (...) {
        Log::DefaultLog.WriteError("Error on LRH Pair Server: unknown exception");
    }

    try {
        socket.close();
    } catch (...) {
    }
    Log::DefaultLog.WriteInfo("LRH Server socket closed");
}

std::string megamol::core::utility::LuaHostService::makeAnswer(const std::string& req) {

    if (req.empty()) return std::string("Null Command.");

    int port;
    this->lastPairPort.store(0);

    this->pairThreads.emplace_back([&]() { this->servePair(); });

    while ((port = this->lastPairPort.load()) == 0 && serverRunning) {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

    vislib::sys::Log::DefaultLog.WriteInfo("LRH: generated PAIR socket on port %i", port);
    return std::to_string(port);
}

std::string megamol::core::utility::LuaHostService::makePairAnswer(const std::string& req) const {
    if (req.empty()) return std::string("Null Command.");

    std::string result;
#ifdef LRH_ANNOYING_DETAILS
    vislib::sys::Log::DefaultLog.WriteInfo("LRH: got request: \"%s\"", req.c_str());
#endif
    int ok = this->GetCoreInstance().GetLuaState()->RunString(req, result);
    if (ok) {
#ifdef LRH_ANNOYING_DETAILS
        vislib::sys::Log::DefaultLog.WriteInfo("LRH: execution is OK and returned\n\"%s\", sending.", result.c_str());
#endif
    } else {
        vislib::sys::Log::DefaultLog.WriteError("LRH: execution is NOT OK and returned Error \"%s\"", result.c_str());
        result = "Error: " + result;
    }
    return result;
}<|endoftext|>


<commit_before>#include "libpldm/base.h"
#include "libpldm/bios.h"
#include "libpldm/pdr.h"
#include "libpldm/platform.h"

#include "common/utils.hpp"
#include "dbus_impl_requester.hpp"
#include "invoker.hpp"
#include "requester/handler.hpp"
#include "requester/request.hpp"

#include <err.h>
#include <getopt.h>
#include <poll.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

#include <sdeventplus/event.hpp>
#include <sdeventplus/source/io.hpp>

#include <cstdio>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <memory>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

#ifdef LIBPLDMRESPONDER
#include "dbus_impl_pdr.hpp"
#include "host-bmc/dbus_to_event_handler.hpp"
#include "host-bmc/dbus_to_host_effecters.hpp"
#include "host-bmc/host_condition.hpp"
#include "host-bmc/host_pdr_handler.hpp"
#include "libpldmresponder/base.hpp"
#include "libpldmresponder/bios.hpp"
#include "libpldmresponder/fru.hpp"
#include "libpldmresponder/oem_handler.hpp"
#include "libpldmresponder/platform.hpp"
#include "xyz/openbmc_project/PLDM/Event/server.hpp"
#endif

#ifdef OEM_IBM
#include "libpldmresponder/file_io.hpp"
#include "libpldmresponder/oem_ibm_handler.hpp"
#endif

constexpr uint8_t MCTP_MSG_TYPE_PLDM = 1;

using namespace pldm;
using namespace sdeventplus;
using namespace sdeventplus::source;
using namespace pldm::responder;
using namespace pldm::utils;

static std::optional<Response>
    processRxMsg(const std::vector<uint8_t>& requestMsg, Invoker& invoker,
                 requester::Handler<requester::Request>& handler)
{
    uint8_t eid = requestMsg[0];
    uint8_t type = requestMsg[1];
    pldm_header_info hdrFields{};
    auto hdr = reinterpret_cast<const pldm_msg_hdr*>(
        requestMsg.data() + sizeof(eid) + sizeof(type));
    if (PLDM_SUCCESS != unpack_pldm_header(hdr, &hdrFields))
    {
        std::cerr << "Empty PLDM request header \n";
        return std::nullopt;
    }

    if (PLDM_RESPONSE != hdrFields.msg_type)
    {
        Response response;
        auto request = reinterpret_cast<const pldm_msg*>(hdr);
        size_t requestLen = requestMsg.size() - sizeof(struct pldm_msg_hdr) -
                            sizeof(eid) - sizeof(type);
        try
        {
            response = invoker.handle(hdrFields.pldm_type, hdrFields.command,
                                      request, requestLen);
        }
        catch (const std::out_of_range& e)
        {
            uint8_t completion_code = PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
            response.resize(sizeof(pldm_msg_hdr));
            auto responseHdr = reinterpret_cast<pldm_msg_hdr*>(response.data());
            pldm_header_info header{};
            header.msg_type = PLDM_RESPONSE;
            header.instance = hdrFields.instance;
            header.pldm_type = hdrFields.pldm_type;
            header.command = hdrFields.command;
            if (PLDM_SUCCESS != pack_pldm_header(&header, responseHdr))
            {
                std::cerr << "Failed adding response header \n";
                return std::nullopt;
            }
            response.insert(response.end(), completion_code);
        }
        return response;
    }
    else if (PLDM_RESPONSE == hdrFields.msg_type)
    {
        auto response = reinterpret_cast<const pldm_msg*>(hdr);
        size_t responseLen = requestMsg.size() - sizeof(struct pldm_msg_hdr) -
                             sizeof(eid) - sizeof(type);
        handler.handleResponse(eid, hdrFields.instance, hdrFields.pldm_type,
                               hdrFields.command, response, responseLen);
    }
    return std::nullopt;
}

void optionUsage(void)
{
    std::cerr << "Usage: pldmd [options]\n";
    std::cerr << "Options:\n";
    std::cerr
        << "  --verbose=<0/1>  0 - Disable verbosity, 1 - Enable verbosity\n";
    std::cerr << "Defaulted settings:  --verbose=0 \n";
}

int main(int argc, char** argv)
{

    bool verbose = false;
    static struct option long_options[] = {
        {"verbose", required_argument, 0, 'v'}, {0, 0, 0, 0}};

    auto argflag = getopt_long(argc, argv, "v:", long_options, nullptr);
    switch (argflag)
    {
        case 'v':
            switch (std::stoi(optarg))
            {
                case 0:
                    verbose = false;
                    break;
                case 1:
                    verbose = true;
                    break;
                default:
                    optionUsage();
                    break;
            }
            break;
        default:
            optionUsage();
            break;
    }

    /* Create local socket. */
    int returnCode = 0;
    int sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
    if (-1 == sockfd)
    {
        returnCode = -errno;
        std::cerr << "Failed to create the socket, RC= " << returnCode << "\n";
        exit(EXIT_FAILURE);
    }

    auto event = Event::get_default();
    auto& bus = pldm::utils::DBusHandler::getBus();
    dbus_api::Requester dbusImplReq(bus, "/xyz/openbmc_project/pldm");

    Invoker invoker{};
    requester::Handler<requester::Request> reqHandler(sockfd, event,
                                                      dbusImplReq, verbose);

#ifdef LIBPLDMRESPONDER
    using namespace pldm::state_sensor;
    dbus_api::Host dbusImplHost(bus, "/xyz/openbmc_project/pldm");
    std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)> pdrRepo(
        pldm_pdr_init(), pldm_pdr_destroy);
    std::unique_ptr<pldm_entity_association_tree,
                    decltype(&pldm_entity_association_tree_destroy)>
        entityTree(pldm_entity_association_tree_init(),
                   pldm_entity_association_tree_destroy);
    std::unique_ptr<pldm_entity_association_tree,
                    decltype(&pldm_entity_association_tree_destroy)>
        bmcEntityTree(pldm_entity_association_tree_init(),
                      pldm_entity_association_tree_destroy);
    std::shared_ptr<HostPDRHandler> hostPDRHandler;
    std::unique_ptr<pldm::host_effecters::HostEffecterParser>
        hostEffecterParser;
    std::unique_ptr<DbusToPLDMEvent> dbusToPLDMEventHandler;
    auto dbusHandler = std::make_unique<DBusHandler>();
    auto hostEID = pldm::utils::readHostEID();
    if (hostEID)
    {
        hostPDRHandler = std::make_shared<HostPDRHandler>(
            sockfd, hostEID, event, pdrRepo.get(), EVENTS_JSONS_DIR,
            entityTree.get(), bmcEntityTree.get(), dbusImplReq, &reqHandler);
        // HostFirmware interface needs access to hostPDR to know if host
        // is running
        dbusImplHost.setHostPdrObj(hostPDRHandler);

        hostEffecterParser =
            std::make_unique<pldm::host_effecters::HostEffecterParser>(
                &dbusImplReq, sockfd, pdrRepo.get(), dbusHandler.get(),
                HOST_JSONS_DIR, &reqHandler);
        dbusToPLDMEventHandler = std::make_unique<DbusToPLDMEvent>(
            sockfd, hostEID, dbusImplReq, &reqHandler);
    }
    std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};

#ifdef OEM_IBM
    std::unique_ptr<pldm::responder::CodeUpdate> codeUpdate =
        std::make_unique<pldm::responder::CodeUpdate>(dbusHandler.get());
    codeUpdate->clearDirPath(LID_STAGING_DIR);
    oemPlatformHandler = std::make_unique<oem_ibm_platform::Handler>(
        dbusHandler.get(), codeUpdate.get(), sockfd, hostEID, dbusImplReq,
        event, &reqHandler);
    codeUpdate->setOemPlatformHandler(oemPlatformHandler.get());
    invoker.registerHandler(PLDM_OEM, std::make_unique<oem_ibm::Handler>(
                                          oemPlatformHandler.get(), sockfd,
                                          hostEID, &dbusImplReq, &reqHandler));
#endif
    invoker.registerHandler(
        PLDM_BIOS, std::make_unique<bios::Handler>(sockfd, hostEID,
                                                   &dbusImplReq, &reqHandler));
    auto fruHandler = std::make_unique<fru::Handler>(
        FRU_JSONS_DIR, FRU_MASTER_JSON, pdrRepo.get(), entityTree.get(),
        bmcEntityTree.get());
    // FRU table is built lazily when a FRU command or Get PDR command is
    // handled. To enable building FRU table, the FRU handler is passed to the
    // Platform handler.
    auto platformHandler = std::make_unique<platform::Handler>(
        dbusHandler.get(), PDR_JSONS_DIR, pdrRepo.get(), hostPDRHandler.get(),
        dbusToPLDMEventHandler.get(), fruHandler.get(),
        oemPlatformHandler.get(), event, true);
#ifdef OEM_IBM
    pldm::responder::oem_ibm_platform::Handler* oemIbmPlatformHandler =
        dynamic_cast<pldm::responder::oem_ibm_platform::Handler*>(
            oemPlatformHandler.get());
    oemIbmPlatformHandler->setPlatformHandler(platformHandler.get());
#endif

    invoker.registerHandler(PLDM_PLATFORM, std::move(platformHandler));
    invoker.registerHandler(
        PLDM_BASE,
        std::make_unique<base::Handler>(hostEID, dbusImplReq, event,
                                        oemPlatformHandler.get(), &reqHandler));
    invoker.registerHandler(PLDM_FRU, std::move(fruHandler));
    dbus_api::Pdr dbusImplPdr(bus, "/xyz/openbmc_project/pldm", pdrRepo.get());
    sdbusplus::xyz::openbmc_project::PLDM::server::Event dbusImplEvent(
        bus, "/xyz/openbmc_project/pldm");

#endif

    pldm::utils::CustomFD socketFd(sockfd);

    struct sockaddr_un addr
    {};
    addr.sun_family = AF_UNIX;
    const char path[] = "\0mctp-mux";
    memcpy(addr.sun_path, path, sizeof(path) - 1);
    int result = connect(socketFd(), reinterpret_cast<struct sockaddr*>(&addr),
                         sizeof(path) + sizeof(addr.sun_family) - 1);
    if (-1 == result)
    {
        returnCode = -errno;
        std::cerr << "Failed to connect to the socket, RC= " << returnCode
                  << "\n";
        exit(EXIT_FAILURE);
    }

    result = write(socketFd(), &MCTP_MSG_TYPE_PLDM, sizeof(MCTP_MSG_TYPE_PLDM));
    if (-1 == result)
    {
        returnCode = -errno;
        std::cerr << "Failed to send message type as pldm to mctp, RC= "
                  << returnCode << "\n";
        exit(EXIT_FAILURE);
    }

    auto callback = [verbose, &invoker, &reqHandler](IO& io, int fd,
                                                     uint32_t revents) {
        if (!(revents & EPOLLIN))
        {
            return;
        }

        // Outgoing message.
        struct iovec iov[2]{};

        // This structure contains the parameter information for the response
        // message.
        struct msghdr msg
        {};

        int returnCode = 0;
        ssize_t peekedLength = recv(fd, nullptr, 0, MSG_PEEK | MSG_TRUNC);
        if (0 == peekedLength)
        {
            // MCTP daemon has closed the socket this daemon is connected to.
            // This may or may not be an error scenario, in either case the
            // recovery mechanism for this daemon is to restart, and hence exit
            // the event loop, that will cause this daemon to exit with a
            // failure code.
            io.get_event().exit(0);
        }
        else if (peekedLength <= -1)
        {
            returnCode = -errno;
            std::cerr << "recv system call failed, RC= " << returnCode << "\n";
        }
        else
        {
            std::vector<uint8_t> requestMsg(peekedLength);
            auto recvDataLength = recv(
                fd, static_cast<void*>(requestMsg.data()), peekedLength, 0);
            if (recvDataLength == peekedLength)
            {
                if (verbose)
                {
                    printBuffer(Rx, requestMsg);
                }

                if (MCTP_MSG_TYPE_PLDM != requestMsg[1])
                {
                    // Skip this message and continue.
                    std::cerr << "Encountered Non-PLDM type message"
                              << "\n";
                }
                else
                {
                    // process message and send response
                    auto response =
                        processRxMsg(requestMsg, invoker, reqHandler);
                    if (response.has_value())
                    {
                        if (verbose)
                        {
                            printBuffer(Tx, *response);
                        }

                        iov[0].iov_base = &requestMsg[0];
                        iov[0].iov_len =
                            sizeof(requestMsg[0]) + sizeof(requestMsg[1]);
                        iov[1].iov_base = (*response).data();
                        iov[1].iov_len = (*response).size();

                        msg.msg_iov = iov;
                        msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);

                        int result = sendmsg(fd, &msg, 0);
                        if (-1 == result)
                        {
                            returnCode = -errno;
                            std::cerr << "sendto system call failed, RC= "
                                      << returnCode << "\n";
                        }
                    }
                }
            }
            else
            {
                std::cerr
                    << "Failure to read peeked length packet. peekedLength= "
                    << peekedLength << " recvDataLength=" << recvDataLength
                    << "\n";
            }
        }
    };

    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
    bus.request_name("xyz.openbmc_project.PLDM");
    IO io(event, socketFd(), EPOLLIN, std::move(callback));
#ifdef LIBPLDMRESPONDER
    if (hostPDRHandler)
    {
        hostPDRHandler->setHostFirmwareCondition();
    }
#endif
    event.loop();

    result = shutdown(sockfd, SHUT_RDWR);
    if (-1 == result)
    {
        returnCode = -errno;
        std::cerr << "Failed to shutdown the socket, RC=" << returnCode << "\n";
        exit(EXIT_FAILURE);
    }
    exit(EXIT_FAILURE);
}
<commit_msg>pldmd: clean up exit logic<commit_after>#include "libpldm/base.h"
#include "libpldm/bios.h"
#include "libpldm/pdr.h"
#include "libpldm/platform.h"

#include "common/utils.hpp"
#include "dbus_impl_requester.hpp"
#include "invoker.hpp"
#include "requester/handler.hpp"
#include "requester/request.hpp"

#include <err.h>
#include <getopt.h>
#include <poll.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

#include <sdeventplus/event.hpp>
#include <sdeventplus/source/io.hpp>

#include <cstdio>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <memory>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

#ifdef LIBPLDMRESPONDER
#include "dbus_impl_pdr.hpp"
#include "host-bmc/dbus_to_event_handler.hpp"
#include "host-bmc/dbus_to_host_effecters.hpp"
#include "host-bmc/host_condition.hpp"
#include "host-bmc/host_pdr_handler.hpp"
#include "libpldmresponder/base.hpp"
#include "libpldmresponder/bios.hpp"
#include "libpldmresponder/fru.hpp"
#include "libpldmresponder/oem_handler.hpp"
#include "libpldmresponder/platform.hpp"
#include "xyz/openbmc_project/PLDM/Event/server.hpp"
#endif

#ifdef OEM_IBM
#include "libpldmresponder/file_io.hpp"
#include "libpldmresponder/oem_ibm_handler.hpp"
#endif

constexpr uint8_t MCTP_MSG_TYPE_PLDM = 1;

using namespace pldm;
using namespace sdeventplus;
using namespace sdeventplus::source;
using namespace pldm::responder;
using namespace pldm::utils;

static std::optional<Response>
    processRxMsg(const std::vector<uint8_t>& requestMsg, Invoker& invoker,
                 requester::Handler<requester::Request>& handler)
{
    uint8_t eid = requestMsg[0];
    uint8_t type = requestMsg[1];
    pldm_header_info hdrFields{};
    auto hdr = reinterpret_cast<const pldm_msg_hdr*>(
        requestMsg.data() + sizeof(eid) + sizeof(type));
    if (PLDM_SUCCESS != unpack_pldm_header(hdr, &hdrFields))
    {
        std::cerr << "Empty PLDM request header \n";
        return std::nullopt;
    }

    if (PLDM_RESPONSE != hdrFields.msg_type)
    {
        Response response;
        auto request = reinterpret_cast<const pldm_msg*>(hdr);
        size_t requestLen = requestMsg.size() - sizeof(struct pldm_msg_hdr) -
                            sizeof(eid) - sizeof(type);
        try
        {
            response = invoker.handle(hdrFields.pldm_type, hdrFields.command,
                                      request, requestLen);
        }
        catch (const std::out_of_range& e)
        {
            uint8_t completion_code = PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
            response.resize(sizeof(pldm_msg_hdr));
            auto responseHdr = reinterpret_cast<pldm_msg_hdr*>(response.data());
            pldm_header_info header{};
            header.msg_type = PLDM_RESPONSE;
            header.instance = hdrFields.instance;
            header.pldm_type = hdrFields.pldm_type;
            header.command = hdrFields.command;
            if (PLDM_SUCCESS != pack_pldm_header(&header, responseHdr))
            {
                std::cerr << "Failed adding response header \n";
                return std::nullopt;
            }
            response.insert(response.end(), completion_code);
        }
        return response;
    }
    else if (PLDM_RESPONSE == hdrFields.msg_type)
    {
        auto response = reinterpret_cast<const pldm_msg*>(hdr);
        size_t responseLen = requestMsg.size() - sizeof(struct pldm_msg_hdr) -
                             sizeof(eid) - sizeof(type);
        handler.handleResponse(eid, hdrFields.instance, hdrFields.pldm_type,
                               hdrFields.command, response, responseLen);
    }
    return std::nullopt;
}

void optionUsage(void)
{
    std::cerr << "Usage: pldmd [options]\n";
    std::cerr << "Options:\n";
    std::cerr
        << "  --verbose=<0/1>  0 - Disable verbosity, 1 - Enable verbosity\n";
    std::cerr << "Defaulted settings:  --verbose=0 \n";
}

int main(int argc, char** argv)
{

    bool verbose = false;
    static struct option long_options[] = {
        {"verbose", required_argument, 0, 'v'}, {0, 0, 0, 0}};

    auto argflag = getopt_long(argc, argv, "v:", long_options, nullptr);
    switch (argflag)
    {
        case 'v':
            switch (std::stoi(optarg))
            {
                case 0:
                    verbose = false;
                    break;
                case 1:
                    verbose = true;
                    break;
                default:
                    optionUsage();
                    break;
            }
            break;
        default:
            optionUsage();
            break;
    }

    /* Create local socket. */
    int returnCode = 0;
    int sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
    if (-1 == sockfd)
    {
        returnCode = -errno;
        std::cerr << "Failed to create the socket, RC= " << returnCode << "\n";
        exit(EXIT_FAILURE);
    }

    auto event = Event::get_default();
    auto& bus = pldm::utils::DBusHandler::getBus();
    dbus_api::Requester dbusImplReq(bus, "/xyz/openbmc_project/pldm");

    Invoker invoker{};
    requester::Handler<requester::Request> reqHandler(sockfd, event,
                                                      dbusImplReq, verbose);

#ifdef LIBPLDMRESPONDER
    using namespace pldm::state_sensor;
    dbus_api::Host dbusImplHost(bus, "/xyz/openbmc_project/pldm");
    std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)> pdrRepo(
        pldm_pdr_init(), pldm_pdr_destroy);
    std::unique_ptr<pldm_entity_association_tree,
                    decltype(&pldm_entity_association_tree_destroy)>
        entityTree(pldm_entity_association_tree_init(),
                   pldm_entity_association_tree_destroy);
    std::unique_ptr<pldm_entity_association_tree,
                    decltype(&pldm_entity_association_tree_destroy)>
        bmcEntityTree(pldm_entity_association_tree_init(),
                      pldm_entity_association_tree_destroy);
    std::shared_ptr<HostPDRHandler> hostPDRHandler;
    std::unique_ptr<pldm::host_effecters::HostEffecterParser>
        hostEffecterParser;
    std::unique_ptr<DbusToPLDMEvent> dbusToPLDMEventHandler;
    auto dbusHandler = std::make_unique<DBusHandler>();
    auto hostEID = pldm::utils::readHostEID();
    if (hostEID)
    {
        hostPDRHandler = std::make_shared<HostPDRHandler>(
            sockfd, hostEID, event, pdrRepo.get(), EVENTS_JSONS_DIR,
            entityTree.get(), bmcEntityTree.get(), dbusImplReq, &reqHandler);
        // HostFirmware interface needs access to hostPDR to know if host
        // is running
        dbusImplHost.setHostPdrObj(hostPDRHandler);

        hostEffecterParser =
            std::make_unique<pldm::host_effecters::HostEffecterParser>(
                &dbusImplReq, sockfd, pdrRepo.get(), dbusHandler.get(),
                HOST_JSONS_DIR, &reqHandler);
        dbusToPLDMEventHandler = std::make_unique<DbusToPLDMEvent>(
            sockfd, hostEID, dbusImplReq, &reqHandler);
    }
    std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};

#ifdef OEM_IBM
    std::unique_ptr<pldm::responder::CodeUpdate> codeUpdate =
        std::make_unique<pldm::responder::CodeUpdate>(dbusHandler.get());
    codeUpdate->clearDirPath(LID_STAGING_DIR);
    oemPlatformHandler = std::make_unique<oem_ibm_platform::Handler>(
        dbusHandler.get(), codeUpdate.get(), sockfd, hostEID, dbusImplReq,
        event, &reqHandler);
    codeUpdate->setOemPlatformHandler(oemPlatformHandler.get());
    invoker.registerHandler(PLDM_OEM, std::make_unique<oem_ibm::Handler>(
                                          oemPlatformHandler.get(), sockfd,
                                          hostEID, &dbusImplReq, &reqHandler));
#endif
    invoker.registerHandler(
        PLDM_BIOS, std::make_unique<bios::Handler>(sockfd, hostEID,
                                                   &dbusImplReq, &reqHandler));
    auto fruHandler = std::make_unique<fru::Handler>(
        FRU_JSONS_DIR, FRU_MASTER_JSON, pdrRepo.get(), entityTree.get(),
        bmcEntityTree.get());
    // FRU table is built lazily when a FRU command or Get PDR command is
    // handled. To enable building FRU table, the FRU handler is passed to the
    // Platform handler.
    auto platformHandler = std::make_unique<platform::Handler>(
        dbusHandler.get(), PDR_JSONS_DIR, pdrRepo.get(), hostPDRHandler.get(),
        dbusToPLDMEventHandler.get(), fruHandler.get(),
        oemPlatformHandler.get(), event, true);
#ifdef OEM_IBM
    pldm::responder::oem_ibm_platform::Handler* oemIbmPlatformHandler =
        dynamic_cast<pldm::responder::oem_ibm_platform::Handler*>(
            oemPlatformHandler.get());
    oemIbmPlatformHandler->setPlatformHandler(platformHandler.get());
#endif

    invoker.registerHandler(PLDM_PLATFORM, std::move(platformHandler));
    invoker.registerHandler(
        PLDM_BASE,
        std::make_unique<base::Handler>(hostEID, dbusImplReq, event,
                                        oemPlatformHandler.get(), &reqHandler));
    invoker.registerHandler(PLDM_FRU, std::move(fruHandler));
    dbus_api::Pdr dbusImplPdr(bus, "/xyz/openbmc_project/pldm", pdrRepo.get());
    sdbusplus::xyz::openbmc_project::PLDM::server::Event dbusImplEvent(
        bus, "/xyz/openbmc_project/pldm");

#endif

    pldm::utils::CustomFD socketFd(sockfd);

    struct sockaddr_un addr
    {};
    addr.sun_family = AF_UNIX;
    const char path[] = "\0mctp-mux";
    memcpy(addr.sun_path, path, sizeof(path) - 1);
    int result = connect(socketFd(), reinterpret_cast<struct sockaddr*>(&addr),
                         sizeof(path) + sizeof(addr.sun_family) - 1);
    if (-1 == result)
    {
        returnCode = -errno;
        std::cerr << "Failed to connect to the socket, RC= " << returnCode
                  << "\n";
        exit(EXIT_FAILURE);
    }

    result = write(socketFd(), &MCTP_MSG_TYPE_PLDM, sizeof(MCTP_MSG_TYPE_PLDM));
    if (-1 == result)
    {
        returnCode = -errno;
        std::cerr << "Failed to send message type as pldm to mctp, RC= "
                  << returnCode << "\n";
        exit(EXIT_FAILURE);
    }

    auto callback = [verbose, &invoker, &reqHandler](IO& io, int fd,
                                                     uint32_t revents) {
        if (!(revents & EPOLLIN))
        {
            return;
        }

        // Outgoing message.
        struct iovec iov[2]{};

        // This structure contains the parameter information for the response
        // message.
        struct msghdr msg
        {};

        int returnCode = 0;
        ssize_t peekedLength = recv(fd, nullptr, 0, MSG_PEEK | MSG_TRUNC);
        if (0 == peekedLength)
        {
            // MCTP daemon has closed the socket this daemon is connected to.
            // This may or may not be an error scenario, in either case the
            // recovery mechanism for this daemon is to restart, and hence exit
            // the event loop, that will cause this daemon to exit with a
            // failure code.
            io.get_event().exit(0);
        }
        else if (peekedLength <= -1)
        {
            returnCode = -errno;
            std::cerr << "recv system call failed, RC= " << returnCode << "\n";
        }
        else
        {
            std::vector<uint8_t> requestMsg(peekedLength);
            auto recvDataLength = recv(
                fd, static_cast<void*>(requestMsg.data()), peekedLength, 0);
            if (recvDataLength == peekedLength)
            {
                if (verbose)
                {
                    printBuffer(Rx, requestMsg);
                }

                if (MCTP_MSG_TYPE_PLDM != requestMsg[1])
                {
                    // Skip this message and continue.
                    std::cerr << "Encountered Non-PLDM type message"
                              << "\n";
                }
                else
                {
                    // process message and send response
                    auto response =
                        processRxMsg(requestMsg, invoker, reqHandler);
                    if (response.has_value())
                    {
                        if (verbose)
                        {
                            printBuffer(Tx, *response);
                        }

                        iov[0].iov_base = &requestMsg[0];
                        iov[0].iov_len =
                            sizeof(requestMsg[0]) + sizeof(requestMsg[1]);
                        iov[1].iov_base = (*response).data();
                        iov[1].iov_len = (*response).size();

                        msg.msg_iov = iov;
                        msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);

                        int result = sendmsg(fd, &msg, 0);
                        if (-1 == result)
                        {
                            returnCode = -errno;
                            std::cerr << "sendto system call failed, RC= "
                                      << returnCode << "\n";
                        }
                    }
                }
            }
            else
            {
                std::cerr
                    << "Failure to read peeked length packet. peekedLength= "
                    << peekedLength << " recvDataLength=" << recvDataLength
                    << "\n";
            }
        }
    };

    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
    bus.request_name("xyz.openbmc_project.PLDM");
    IO io(event, socketFd(), EPOLLIN, std::move(callback));
#ifdef LIBPLDMRESPONDER
    if (hostPDRHandler)
    {
        hostPDRHandler->setHostFirmwareCondition();
    }
#endif
    returnCode = event.loop();

    if (shutdown(sockfd, SHUT_RDWR))
    {
        std::perror("Failed to shutdown the socket");
    }
    if (returnCode)
    {
        exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS);
}
<|endoftext|>