Skip to content
Merged
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
3 changes: 3 additions & 0 deletions src/hex_sdk_library/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ SUBDIRS += dryrun
SUBDIRS += crypto
SUBDIRS += license

# Unit tests for sources built directly into the SDK library (e.g. exec.cpp).
SUBDIRS += tests

LIB = $(HEX_SDK_LIB_ARCHIVE)

LIB_SRCS = exec.cpp
Expand Down
11 changes: 11 additions & 0 deletions src/hex_sdk_library/exec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ Exec(const Cmd& command, bool daemonize)

// child process
if (pid == 0) {
// Redirect stdin to /dev/null so the child doesn't inherit a serial console
// (e.g. IPMI SoL /dev/ttyS0) as stdin; tools like mongosh would otherwise
// block opening the controlling TTY. Mirrors the stdout/stderr handling below.
int devNullIn = open("/dev/null", O_RDONLY);
if (devNullIn == -1 || dup2(devNullIn, STDIN_FILENO) == -1) {
_exit(EXIT_FAILURE);
}
if (devNullIn > STDIN_FILENO) {
close(devNullIn);
}

if (command.captureStdout) {
// close the read-ends of the pipe, child only needs to write
if (close(stdoutPipe[0]) == -1) {
Expand Down
7 changes: 7 additions & 0 deletions src/hex_sdk_library/tests/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# HEX SDK

include ../../../../build.mk

TESTS_LIBS = $(HEX_SDK_LIB_ARCHIVE)

include $(HEX_MAKEDIR)/hex_sdk.mk
42 changes: 42 additions & 0 deletions src/hex_sdk_library/tests/test_exec_stdin_01.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// HEX SDK

#include <fcntl.h>
#include <unistd.h>

#include <string>

#include <hex/exec.hpp>
#include <hex/test.h>

// Verify Exec() detaches the child's stdin to /dev/null instead of letting it
// inherit the parent's stdin. This guards the serial-console hang fix: we give
// the test process (the parent) a recognizable, non-/dev/null stdin and confirm
// the child still sees /dev/null. Without the fix the child would inherit the
// parent's fd and the assertion below would fail.
int main()
{
// Point the parent's stdin at something that is clearly not /dev/null.
int fd = open("/dev/zero", O_RDONLY);
HEX_TEST_FATAL(fd != -1);
HEX_TEST_FATAL(dup2(fd, STDIN_FILENO) != -1);
if (fd > STDIN_FILENO) {
close(fd);
}

// The child reports what its own stdin (fd 0) points to.
const ExecSyncResult result =
ExecBashSync(10, true /*captureStdout*/, false /*captureStderr*/, {}, "readlink /proc/self/fd/0");

HEX_TEST(result.exitCode == 0);
HEX_TEST(!result.isTimedOut);

// Strip the trailing newline from readlink's output.
std::string target = result.stdoutOutput;
while (!target.empty() && (target.back() == '\n' || target.back() == '\r')) {
target.pop_back();
}

HEX_TEST(target == "/dev/null");

return HexTestResult;
}