Move to process v2
Boost process v2 brings some significant benefits to our launching of
processes[1]. In bmcweb terms:
1. The code is radically simpler, which decreaeses compile times, and
reduces the scope for code scanning tools.
2. The code now uses standard asio pipes instead of inventing its own.
3. Separate compilation.
Tested:
We don't have a lot of unit tests for the virtual media stuff that I can
run, but we do have unit tests for credentials pipe, which in this
change have been ported over, so the feature works. Unit tests are
passing.
[1] https://www.boost.org/doc/libs/1_80_0/doc/html/boost_process/v2.html#boost_process.v2.introduction
Change-Id: Ia20226819d75ff6e492f8852185f0b73e8f5cf83
Signed-off-by: Ed Tanous <ed@tanous.net>
diff --git a/include/credential_pipe.hpp b/include/credential_pipe.hpp
index 169d47c..2cc3dc8 100644
--- a/include/credential_pipe.hpp
+++ b/include/credential_pipe.hpp
@@ -1,9 +1,13 @@
#pragma once
+#include "logging.hpp"
+
#include <boost/asio/buffer.hpp>
+#include <boost/asio/connect_pipe.hpp>
#include <boost/asio/io_context.hpp>
+#include <boost/asio/readable_pipe.hpp>
+#include <boost/asio/writable_pipe.hpp>
#include <boost/asio/write.hpp>
-#include <boost/process/async_pipe.hpp>
#include <array>
#include <string>
@@ -12,7 +16,15 @@
class CredentialsPipe
{
public:
- explicit CredentialsPipe(boost::asio::io_context& io) : impl(io) {}
+ explicit CredentialsPipe(boost::asio::io_context& io) : impl(io), read(io)
+ {
+ boost::system::error_code ec;
+ boost::asio::connect_pipe(read, impl, ec);
+ if (ec)
+ {
+ BMCWEB_LOG_CRITICAL("Failed to connect pipe {}", ec.what());
+ }
+ }
CredentialsPipe(const CredentialsPipe&) = delete;
CredentialsPipe(CredentialsPipe&&) = delete;
@@ -25,9 +37,9 @@
explicit_bzero(pass.data(), pass.capacity());
}
- int fd() const
+ int releaseFd()
{
- return impl.native_source();
+ return read.release();
}
template <typename WriteHandler>
@@ -44,7 +56,8 @@
std::forward<WriteHandler>(handler));
}
- boost::process::async_pipe impl;
+ boost::asio::writable_pipe impl;
+ boost::asio::readable_pipe read;
private:
std::string user;
diff --git a/include/vm_websocket.hpp b/include/vm_websocket.hpp
index 19054a6..3a72b3a 100644
--- a/include/vm_websocket.hpp
+++ b/include/vm_websocket.hpp
@@ -3,10 +3,11 @@
#include "app.hpp"
#include "websocket.hpp"
+#include <boost/asio/readable_pipe.hpp>
+#include <boost/asio/writable_pipe.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
-#include <boost/process/async_pipe.hpp>
-#include <boost/process/child.hpp>
-#include <boost/process/io.hpp>
+#include <boost/process/v2/process.hpp>
+#include <boost/process/v2/stdio.hpp>
#include <csignal>
@@ -26,8 +27,11 @@
class Handler : public std::enable_shared_from_this<Handler>
{
public:
- Handler(const std::string& mediaIn, boost::asio::io_context& ios) :
- pipeOut(ios), pipeIn(ios), media(mediaIn),
+ Handler(const std::string& media, boost::asio::io_context& ios) :
+ pipeOut(ios), pipeIn(ios),
+ proxy(ios, "/usr/bin/nbd-proxy", {media},
+ boost::process::v2::process_stdio{
+ .in = pipeIn, .out = pipeOut, .err = nullptr}),
outputBuffer(new boost::beast::flat_static_buffer<nbdBufferSize>),
inputBuffer(new boost::beast::flat_static_buffer<nbdBufferSize>)
{}
@@ -56,9 +60,6 @@
void connect()
{
std::error_code ec;
- proxy = boost::process::child("/usr/bin/nbd-proxy", media,
- boost::process::std_out > pipeOut,
- boost::process::std_in < pipeIn, ec);
if (ec)
{
BMCWEB_LOG_ERROR("Couldn't connect to nbd-proxy: {}", ec.message());
@@ -148,10 +149,9 @@
});
}
- boost::process::async_pipe pipeOut;
- boost::process::async_pipe pipeIn;
- boost::process::child proxy;
- std::string media;
+ boost::asio::readable_pipe pipeOut;
+ boost::asio::writable_pipe pipeIn;
+ boost::process::v2::process proxy;
bool doingWrite{false};
std::unique_ptr<boost::beast::flat_static_buffer<nbdBufferSize>>
diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
index 529b9dc..37fb204 100644
--- a/redfish-core/lib/virtual_media.hpp
+++ b/redfish-core/lib/virtual_media.hpp
@@ -25,7 +25,6 @@
#include "registries/privilege_registry.hpp"
#include "utils/json_utils.hpp"
-#include <boost/process/async_pipe.hpp>
#include <boost/url/format.hpp>
#include <boost/url/url_view.hpp>
#include <boost/url/url_view_base.hpp>
@@ -470,7 +469,7 @@
// Open pipe
secretPipe = std::make_shared<CredentialsPipe>(
crow::connections::systemBus->get_io_context());
- fd = secretPipe->fd();
+ fd = secretPipe->releaseFd();
// Pass secret over pipe
secretPipe->asyncWrite(
diff --git a/test/include/credential_pipe_test.cpp b/test/include/credential_pipe_test.cpp
index 0c72222..3a750b4 100644
--- a/test/include/credential_pipe_test.cpp
+++ b/test/include/credential_pipe_test.cpp
@@ -3,8 +3,9 @@
#include <unistd.h>
#include <boost/asio/io_context.hpp>
-#include <boost/beast/core/file_posix.hpp>
-#include <boost/system/detail/error_code.hpp>
+#include <boost/asio/read.hpp>
+#include <boost/asio/readable_pipe.hpp>
+#include <boost/system/error_code.hpp>
#include <array>
#include <cstddef>
@@ -26,21 +27,20 @@
TEST(CredentialsPipe, BasicSend)
{
- boost::beast::file_posix file;
+ boost::asio::io_context io;
+ boost::asio::readable_pipe testPipe(io);
{
- boost::asio::io_context io;
CredentialsPipe pipe(io);
- file.native_handle(dup(pipe.fd()));
- ASSERT_GT(file.native_handle(), 0);
+ testPipe = boost::asio::readable_pipe(io, pipe.releaseFd());
+ ASSERT_GT(testPipe.native_handle(), 0);
pipe.asyncWrite("username", "password",
std::bind_front(handler, std::ref(io)));
- io.run();
}
+ io.run();
std::array<char, 18> buff{};
boost::system::error_code ec;
- size_t r = file.read(buff.data(), buff.size(), ec);
+ boost::asio::read(testPipe, boost::asio::buffer(buff), ec);
ASSERT_FALSE(ec);
- ASSERT_EQ(r, 18);
EXPECT_THAT(buff,
ElementsAre('u', 's', 'e', 'r', 'n', 'a', 'm', 'e', '\0', 'p',