user-mgr: convert boost::process::child to fork/execv
Boost process v1 is deprecated and removed in the next version
of Boost. process v2 is async-only and cannot easily be replaced
with the current usages. Switch to use raw fork/execv calls instead,
which is what boost would have done internally.
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: Ia802cc5428b37dbe428bf1f8befcac168ff3262b
diff --git a/test/execute_cmd_test.cpp b/test/execute_cmd_test.cpp
index 8e38ce8..685899d 100644
--- a/test/execute_cmd_test.cpp
+++ b/test/execute_cmd_test.cpp
@@ -27,8 +27,9 @@
TEST(ExecuteCmdTest, NonExistentCommand)
{
- EXPECT_THROW(phosphor::user::executeCmd("/path/to/nonexistent_command"),
- boost::process::process_error);
+ EXPECT_THROW(
+ phosphor::user::executeCmd("/path/to/nonexistent_command"),
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure);
}
TEST(ExecuteCmdTest, CommandReturnsNonZeroExitCode)
diff --git a/user_mgr.hpp b/user_mgr.hpp
index 13f3c8e..27c5d41 100644
--- a/user_mgr.hpp
+++ b/user_mgr.hpp
@@ -17,8 +17,9 @@
#include "json_serializer.hpp"
#include "users.hpp"
-#include <boost/process/child.hpp>
-#include <boost/process/io.hpp>
+#include <sys/wait.h>
+#include <unistd.h>
+
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/elog.hpp>
#include <phosphor-logging/lg2.hpp>
@@ -93,30 +94,108 @@
template <typename... ArgTypes>
std::vector<std::string> executeCmd(const char* path, ArgTypes&&... tArgs)
{
- std::vector<std::string> stdOutput;
- boost::process::ipstream stdOutStream;
- boost::process::child execProg(path, const_cast<char*>(tArgs)...,
- boost::process::std_out > stdOutStream);
- std::string stdOutLine;
+ int pipefd[2];
- while (stdOutStream && std::getline(stdOutStream, stdOutLine) &&
- !stdOutLine.empty())
+ if (pipe(pipefd) == -1)
{
- stdOutput.emplace_back(stdOutLine);
+ lg2::error("Failed to create pipe: {ERROR}", "ERROR", strerror(errno));
+ phosphor::logging::elog<
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
+ return {};
}
- execProg.wait();
+ pid_t pid = fork();
- int retCode = execProg.exit_code();
- if (retCode)
+ if (pid == -1)
+ {
+ lg2::error("Failed to fork process: {ERROR}", "ERROR", strerror(errno));
+ phosphor::logging::elog<
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
+ close(pipefd[0]); // Close read end of pipe
+ close(pipefd[1]); // Close write end of pipe
+ return {};
+ }
+
+ if (pid == 0) // Child process
+ {
+ close(pipefd[0]); // Close read end of pipe
+
+ // Redirect write end of the pipe to stdout.
+ if (dup2(pipefd[1], STDOUT_FILENO) == -1)
+ {
+ lg2::error("Failed to redirect stdout: {ERROR}", "ERROR",
+ strerror(errno));
+ _exit(EXIT_FAILURE);
+ }
+ close(pipefd[1]); // Close write end of pipe
+
+ std::vector<const char*> args = {path};
+ (args.emplace_back(const_cast<const char*>(tArgs)), ...);
+ args.emplace_back(nullptr);
+
+ execv(path, const_cast<char* const*>(args.data()));
+
+ // If exec returns, an error occurred
+ lg2::error("Failed to execute command '{PATH}': {ERROR}", "PATH", path,
+ "ERROR", strerror(errno));
+ _exit(EXIT_FAILURE);
+ }
+
+ // Parent process.
+
+ close(pipefd[1]); // Close write end of pipe
+
+ FILE* fp = fdopen(pipefd[0], "r");
+ if (fp == nullptr)
+ {
+ lg2::error("Failed to open pipe for reading: {ERROR}", "ERROR",
+ strerror(errno));
+ close(pipefd[0]);
+ phosphor::logging::elog<
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
+ return {};
+ }
+
+ std::vector<std::string> results;
+ char buffer[256];
+ while (fgets(buffer, sizeof(buffer), fp) != nullptr)
+ {
+ std::string line = buffer;
+ if (!line.empty() && line.back() == '\n')
+ {
+ line.pop_back(); // Remove newline character
+ }
+ results.emplace_back(line);
+ }
+
+ fclose(fp);
+ close(pipefd[0]);
+
+ int status;
+ while (waitpid(pid, &status, 0) == -1)
+ {
+ // Need to retry on EINTR.
+ if (errno == EINTR)
+ {
+ continue;
+ }
+
+ lg2::error("Failed to wait for child process: {ERROR}", "ERROR",
+ strerror(errno));
+ phosphor::logging::elog<
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
+ return {};
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
{
lg2::error("Command {PATH} execution failed, return code {RETCODE}",
- "PATH", path, "RETCODE", retCode);
+ "PATH", path, "RETCODE", WEXITSTATUS(status));
phosphor::logging::elog<
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
}
- return stdOutput;
+ return results;
}
/** @class UserMgr