Skip to content

Commit ad54928

Browse files
yfeldblumfacebook-github-bot
authored andcommittedMar 10, 2025·
let Subprocess accept a sigmask for the child process
Reviewed By: dmm-fb Differential Revision: D70640704 fbshipit-source-id: 8202b82003c9809e772202fc724d962ee22dbd46
1 parent 23042e2 commit ad54928

File tree

3 files changed

+96
-1
lines changed

3 files changed

+96
-1
lines changed
 

‎folly/Subprocess.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ void Subprocess::spawnInternal(
628628
args.envv = env ? envHolder.get() : environ;
629629
args.executable = executable;
630630
args.err = err;
631-
args.oldSignals = oldSignals;
631+
args.oldSignals = options.sigmask_.value_or(oldSignals);
632632

633633
// Child is alive. We have to be very careful about throwing after this
634634
// point. We are inside the constructor, so if we throw the Subprocess

‎folly/Subprocess.h

+6
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,11 @@ class Subprocess {
547547
}
548548
#endif
549549

550+
Options& setSignalMask(sigset_t sigmask) {
551+
sigmask_ = sigmask;
552+
return *this;
553+
}
554+
550555
Options& addPrintPidToBuffer(span<char> buf);
551556

552557
private:
@@ -570,6 +575,7 @@ class Subprocess {
570575
#if defined(__linux__)
571576
Optional<cpu_set_t> cpuSet_;
572577
#endif
578+
Optional<sigset_t> sigmask_;
573579
std::unordered_set<char*> setPrintPidToBuffer_;
574580
};
575581

‎folly/test/SubprocessTest.cpp

+89
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,62 @@ bool waitForAnyOutput(Subprocess& proc) {
7070
LOG(INFO) << "Read " << buffer;
7171
return len == 1;
7272
}
73+
74+
sigset_t makeSignalMask(folly::span<int const> signals) {
75+
sigset_t sigmask;
76+
sigemptyset(&sigmask);
77+
for (auto sig : signals) {
78+
sigaddset(&sigmask, sig);
79+
}
80+
return sigmask;
81+
}
82+
83+
struct ScopedSignalMaskOverride {
84+
sigset_t sigmask;
85+
explicit ScopedSignalMaskOverride(folly::span<int const> signals) {
86+
auto target = makeSignalMask(signals);
87+
PCHECK(0 == pthread_sigmask(SIG_SETMASK, &target, &sigmask));
88+
}
89+
~ScopedSignalMaskOverride() {
90+
PCHECK(0 == pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
91+
}
92+
};
93+
94+
uint64_t readSignalMask(sigset_t sigmask) {
95+
static_assert(NSIG - 1 <= 64); // 0 is not a signal
96+
uint64_t ret = 0;
97+
for (int sig = 1; sig < NSIG; ++sig) {
98+
if (sigismember(&sigmask, sig)) {
99+
ret |= (uint64_t(1) << (sig - 1));
100+
}
101+
}
102+
return ret;
103+
}
104+
105+
sigset_t getCurrentSignalMask() {
106+
sigset_t sigmask;
107+
pthread_sigmask(SIG_SETMASK, nullptr, &sigmask);
108+
return sigmask;
109+
}
110+
111+
std::string_view readOneLineOfProcSelfStatus(
112+
std::string_view text, std::string_view key) {
113+
std::vector<std::string_view> lines;
114+
folly::split('\n', text, lines);
115+
auto prefix = fmt::format("{}:", key);
116+
auto iter = std::find_if(lines.begin(), lines.end(), [&](auto line) {
117+
return folly::StringPiece(line).starts_with(prefix);
118+
});
119+
if (iter == lines.end()) {
120+
return {};
121+
}
122+
auto line = *iter;
123+
line.remove_prefix(prefix.size());
124+
while (!line.empty() && std::isspace(line[0])) {
125+
line.remove_prefix(1);
126+
}
127+
return line;
128+
}
73129
} // namespace
74130

75131
struct SubprocessFdActionsListTest : testing::Test {};
@@ -970,3 +1026,36 @@ TEST(WritePidIntoBufTest, WritesPidIntoBufExampleEnvVar) {
9701026
folly::split('\n', p.first, lines);
9711027
EXPECT_THAT(lines, testing::Contains(fmt::format("{}{}", prefix, pid)));
9721028
}
1029+
1030+
#if defined(__linux__)
1031+
1032+
TEST(SetSignalMask, KeepsExistingMask) {
1033+
// the /proc filesystem, including /proc/self/status, is linux-specific
1034+
ASSERT_EQ(0, readSignalMask(getCurrentSignalMask()));
1035+
ScopedSignalMaskOverride guard{std::array{SIGURG, SIGCHLD}};
1036+
auto options = Subprocess::Options().pipeStdout();
1037+
Subprocess proc(
1038+
std::vector<std::string>{"/bin/cat", "/proc/self/status"}, options);
1039+
auto p = proc.communicate();
1040+
proc.wait();
1041+
auto line = readOneLineOfProcSelfStatus(p.first, "SigBlk");
1042+
auto expected = (1 << (SIGURG - 1)) | (1 << (SIGCHLD - 1));
1043+
EXPECT_EQ(fmt::format("{:016x}", expected), line);
1044+
}
1045+
1046+
TEST(SetSignalMask, CanOverrideExistingMask) {
1047+
// the /proc filesystem, including /proc/self/status, is linux-specific
1048+
ASSERT_EQ(0, readSignalMask(getCurrentSignalMask()));
1049+
ScopedSignalMaskOverride guard{std::array{SIGURG, SIGCHLD}};
1050+
auto sigmask = makeSignalMask(std::array{SIGUSR1, SIGUSR2});
1051+
auto options = Subprocess::Options().pipeStdout().setSignalMask(sigmask);
1052+
Subprocess proc(
1053+
std::vector<std::string>{"/bin/cat", "/proc/self/status"}, options);
1054+
auto p = proc.communicate();
1055+
proc.wait();
1056+
auto line = readOneLineOfProcSelfStatus(p.first, "SigBlk");
1057+
auto expected = (1 << (SIGUSR1 - 1)) | (1 << (SIGUSR2 - 1));
1058+
EXPECT_EQ(fmt::format("{:016x}", expected), line);
1059+
}
1060+
1061+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.