Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Unix domain sockets when targeting localhost #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/proxyfmu/lib_info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct version
int patch = 0;
};

/// Returns the version of the libcosim library.
/// Returns the version of the proxyfmu library.
version library_version();

} // namespace proxyfmu
Expand Down
2 changes: 0 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ set(generatedSourcesDir "${CMAKE_BINARY_DIR}/generated")

set(commonPublicHeaders

"proxyfmu/fixed_range_random_generator.hpp"
"proxyfmu/fs_portability.hpp"
"proxyfmu/lib_info.hpp"
"proxyfmu/remote_info.hpp"
"proxyfmu/temp_dir.hpp"

)

Expand Down
4 changes: 3 additions & 1 deletion src/proxyfmu/client/proxy_fmu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ proxy_fmu::proxy_fmu(const filesystem::path& fmuPath, std::optional<remote_info>
, remote_(std::move(remote))
, modelDescription_(fmilibcpp::loadFmu(fmuPath)->get_model_description())
{
if (!exists(fmuPath)) throw std::runtime_error("No such file: " + filesystem::absolute(fmuPath).string() + "!");
if (!exists(fmuPath)) {
throw std::runtime_error("No such file: " + filesystem::absolute(fmuPath).string() + "!");
}
}

const fmilibcpp::model_description& proxy_fmu::get_model_description() const
Expand Down
28 changes: 16 additions & 12 deletions src/proxyfmu/client/proxy_slave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,23 @@ proxy_slave::proxy_slave(
: fmilibcpp::slave(instanceName)
, modelDescription_(std::move(modelDescription))
{
int port = -1;
std::string host;

std::shared_ptr<TTransport> socket;
if (!remote) {
host = "localhost";
std::mutex mtx;
std::condition_variable cv;
thread_ = std::make_unique<std::thread>(&start_process, fmuPath, instanceName, std::ref(port), std::ref(mtx), std::ref(cv));
std::string bind;
thread_ = std::make_unique<std::thread>(&start_process, fmuPath, instanceName, std::ref(bind), std::ref(mtx), std::ref(cv), true);
std::unique_lock<std::mutex> lck(mtx);
while (port == -1) cv.wait(lck);
while (bind.empty()) cv.wait(lck);
if (bind != "-") {
socket = std::make_shared<TSocket>(bind);
}

} else {
host = remote->host;
std::shared_ptr<TTransport> socket(new TSocket(host, remote->port));
auto transport = std::make_shared<TFramedTransport>(socket);
std::string host = remote->host;
std::shared_ptr<TTransport> tmp_socket(new TSocket(host, remote->port));
auto transport = std::make_shared<TFramedTransport>(tmp_socket);
std::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
auto client = std::make_shared<BootServiceClient>(protocol);
transport->open();
Expand All @@ -73,17 +76,18 @@ proxy_slave::proxy_slave(
read_data(fmuPath.string(), data);

const std::string fmuName = proxyfmu::filesystem::path(fmuPath).stem().string();
port = client->loadFromBinaryData(fmuName, instanceName, data);
int port = client->loadFromBinaryData(fmuName, instanceName, data);
transport->close();

socket = std::make_shared<TSocket>("localhost", port);
}

if (port == -999) {
if (!socket) {
if (thread_) thread_->join();
throw std::runtime_error("[proxyfmu] Unable to bind to external proxy process!");
}

std::shared_ptr<TTransport> socket(new TSocket(host, port));
transport_ = std::make_shared<TFramedTransport>(socket);
transport_ = std::make_shared<TBufferedTransport>(socket);
std::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport_));
client_ = std::make_shared<FmuServiceClient>(protocol);
transport_->open();
Expand Down
2 changes: 1 addition & 1 deletion src/proxyfmu/lib_info.cpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ version library_version()
// clang-format on
}

}
}
31 changes: 15 additions & 16 deletions src/proxyfmu/process_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@
namespace proxyfmu
{

std::string boolToStr(bool b)
{
return b ? "true" : "false";
}

void start_process(
const proxyfmu::filesystem::path& fmuPath,
const std::string& instanceName,
int& port,
std::string& bind,
std::mutex& mtx,
std::condition_variable& cv)
std::condition_variable& cv,
bool localhost)
{

proxyfmu::filesystem::path executable;
Expand All @@ -31,16 +37,6 @@ void start_process(
executable = "proxyfmu.exe";
#endif

if (!proxyfmu::filesystem::exists(executable)) {
boost::dll::fs::error_code ec;
boost::dll::fs::path loc = boost::dll::program_location(ec);
if (!ec.failed()) {
executable = loc.parent_path().string() / executable;
} else {
std::cerr << "[proxyfmu] Error, unable to locate parent executable" << std::endl;
}
}

if (!proxyfmu::filesystem::exists(executable)) {
auto execPath = proxyfmu::filesystem::absolute(executable).string();
throw std::runtime_error("[proxyfmu] No proxyfmu executable found. " + execPath + " does not exist!");
Expand All @@ -59,7 +55,7 @@ void start_process(
std::cout << "\n";
std::cout << "[proxyfmu] Booting FMU instance '" << instanceName << "'.." << std::endl;

std::string cmd(execStr + " --fmu \"" + fmuPath.string() + "\" --instanceName " + instanceName);
std::string cmd(execStr + " --fmu \"" + fmuPath.string() + "\" --instanceName " + instanceName + " --localhost " + boolToStr(localhost));

boost::process::ipstream pipe_stream;
boost::process::child c(cmd, boost::process::std_out > pipe_stream);
Expand All @@ -70,8 +66,11 @@ void start_process(
if (!bound && line.substr(0, 16) == "[proxyfmu] port=") {
{
std::lock_guard<std::mutex> lck(mtx);
port = std::stoi(line.substr(16));
std::cout << "[proxyfmu] FMU instance '" << instanceName << "' instantiated using port " << port << std::endl;
bind = line.substr(16);
if (bind.back() == '\r' || bind.back() == '\n') {
bind.pop_back();
}
std::cout << "[proxyfmu] FMU instance '" << instanceName << "' instantiated and bound to " << bind << std::endl;
}
cv.notify_one();
bound = true;
Expand All @@ -92,7 +91,7 @@ void start_process(
<< instanceName << "' returned with status "
<< std::to_string(status) << ". Unable to bind.." << std::endl;
std::lock_guard<std::mutex> lck(mtx);
port = -999;
bind = "-";

cv.notify_one();
}
Expand Down
8 changes: 4 additions & 4 deletions tool/booter/boot_service_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@ int32_t boot_service_handler::loadFromBinaryData(const std::string& fmuName, con

write_data(fmuPath, data);

int port = -1;
std::string bind;
std::mutex mtx;
std::condition_variable cv;
auto t = std::make_unique<std::thread>(&start_process, fmuPath, instanceName, std::ref(port), std::ref(mtx), std::ref(cv));
auto t = std::make_unique<std::thread>(&start_process, fmuPath, instanceName, std::ref(bind), std::ref(mtx), std::ref(cv) , false);
processes_.emplace_back(std::move(t));
dirs_.emplace_back(std::move(tmp));

std::unique_lock<std::mutex> lck(mtx);
while (port == -1) cv.wait(lck);
while (bind.empty()) cv.wait(lck);

return port;
return std::stoi(bind);
}

boot_service_handler::~boot_service_handler()
Expand Down
6 changes: 3 additions & 3 deletions tool/booter/boot_service_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
#ifndef PROXYFMU_BOOT_SERVICE_HANDLER_HPP
#define PROXYFMU_BOOT_SERVICE_HANDLER_HPP

#include <proxyfmu/fixed_range_random_generator.hpp>
#include <proxyfmu/temp_dir.hpp>
#include <proxyfmu/thrift/BootService.h>
#include "../temp_dir.hpp"

#include "proxyfmu/thrift/BootService.h"

#include <memory>
#include <thread>
Expand Down
138 changes: 108 additions & 30 deletions tool/proxyfmu.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@

#include "fixed_range_random_generator.hpp"
#include "fmu_service_handler.hpp"

#include <proxyfmu/fixed_range_random_generator.hpp>
#include <proxyfmu/fs_portability.hpp>
#include <proxyfmu/lib_info.hpp>
#include "proxyfmu/fs_portability.hpp"
#include "proxyfmu/lib_info.hpp"

#include <boost/program_options.hpp>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransportUtils.h>

#include <functional>
#include <ios>
#include <iostream>
#include <random>
#include <utility>

using namespace proxyfmu::thrift;
Expand All @@ -25,6 +27,61 @@ using namespace ::apache::thrift::transport;
namespace
{

class raii_path
{

public:
explicit raii_path(proxyfmu::filesystem::path path)
: path_(std::move(path))
{ }

[[nodiscard]] std::string string() const
{
return path_.string();
}

~raii_path()
{
proxyfmu::filesystem::remove_all(path_);
}

private:
proxyfmu::filesystem::path path_;
};

std::string generate_uuid()
{
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<> dis(0, 15);
static std::uniform_int_distribution<> dis2(8, 11);

int i;
std::stringstream ss;
ss << std::hex;
for (i = 0; i < 8; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 4; i++) {
ss << dis(gen);
}
ss << "-4";
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
ss << dis2(gen);
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 12; i++) {
ss << dis(gen);
}
return ss.str();
}

class ServerReadyEventHandler : public TServerEventHandler
{

Expand All @@ -51,7 +108,7 @@ const int SUCCESS = 0;
const int COMMANDLINE_ERROR = 1;
const int UNHANDLED_ERROR = 2;

int run_application(const std::string& fmu, const std::string& instanceName)
int run_application(const std::string& fmu, const std::string& instanceName, bool localhost)
{
std::unique_ptr<TSimpleServer> server;
auto stop = [&]() {
Expand All @@ -60,43 +117,63 @@ int run_application(const std::string& fmu, const std::string& instanceName)
std::shared_ptr<fmu_service_handler> handler(new fmu_service_handler(fmu, instanceName, stop));
std::shared_ptr<TProcessor> processor(new FmuServiceProcessor(handler));

std::shared_ptr<TTransportFactory> transportFactory(new TFramedTransportFactory());
std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

proxyfmu::fixed_range_random_generator rng(port_range_min, port_range_max);
if (!localhost) {
proxyfmu::fixed_range_random_generator rng(port_range_min, port_range_max);

int port;
int final_port = -1;
for (auto i = 0; i < max_port_retries; i++) {
port = rng.next();

std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
server = std::make_unique<TSimpleServer>(processor, serverTransport, transportFactory, protocolFactory);
server->setServerEventHandler(std::make_shared<ServerReadyEventHandler>([port, &final_port] {
final_port = port;
std::cout << "[proxyfmu] port=" << std::to_string(final_port) << std::endl;
}));

try {

int port;
int final_port = -1;
for (auto i = 0; i < max_port_retries; i++) {
port = rng.next();
server->serve();
break;

} catch (TTransportException& ex) {
std::cout << "[proxyfmu] " << ex.what()
<< ". Failed to bind to port " << std::to_string(port)
<< ". Retrying with another one. Attempt " << std::to_string(i + 1)
<< " of " << std::to_string(max_port_retries) << ".." << std::endl;
}
}

if (final_port != -1) {
return SUCCESS;
} else {
std::cerr << "[proxyfmu] Unable to bind after max number of retries.." << std::endl;
return UNHANDLED_ERROR;
}

std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
} else {

raii_path uds{"uds_" + generate_uuid() + ".binary.thrift"};
std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(uds.string()));
server = std::make_unique<TSimpleServer>(processor, serverTransport, transportFactory, protocolFactory);
server->setServerEventHandler(std::make_shared<ServerReadyEventHandler>([port, &final_port] {
final_port = port;
std::cout << "[proxyfmu] port=" << std::to_string(final_port) << std::endl;

server->setServerEventHandler(std::make_shared<ServerReadyEventHandler>([&uds] {
std::cout << "[proxyfmu] port=" << uds.string() << std::endl;
}));

try {

server->serve();
break;

return SUCCESS;
} catch (TTransportException& ex) {
std::cout << "[proxyfmu] " << ex.what()
<< ". Failed to bind to port " << std::to_string(port)
<< ". Retrying with another one. Attempt " << std::to_string(i + 1)
<< " of " << std::to_string(max_port_retries) << ".." << std::endl;
std::cerr << "[proxyfmu] " << ex.what() << std::endl;
return UNHANDLED_ERROR;
}
}

if (final_port != -1) {
return SUCCESS;
} else {
std::cerr << "[proxyfmu] Unable to bind after max number of retries.." << std::endl;
return UNHANDLED_ERROR;
}

}

int printHelp(boost::program_options::options_description& desc)
Expand Down Expand Up @@ -125,6 +202,7 @@ int main(int argc, char** argv)
desc.add_options()("version,v", "Print program version.");
desc.add_options()("fmu", po::value<std::string>()->required(), "Location of the fmu to load.");
desc.add_options()("instanceName", po::value<std::string>()->required(), "Name of the slave instance.");
desc.add_options()("localhost", po::value<bool>()->required(), "Running on localhost?");

if (argc == 1) {
return printHelp(desc);
Expand Down Expand Up @@ -161,7 +239,7 @@ int main(int argc, char** argv)

const auto instanceName = vm["instanceName"].as<std::string>();

return run_application(fmu, instanceName);
return run_application(fmu, instanceName, vm["localhost"].as<bool>());

} catch (const std::exception& e) {
std::cerr << "[proxyfmu] Unhandled Exception reached the top of main: " << e.what() << ", application will now exit" << std::endl;
Expand Down
Loading