Skip to content

Commit a8d3a36

Browse files
committed
Add stderr support to pipe
1 parent 6a03bcb commit a8d3a36

4 files changed

Lines changed: 81 additions & 3 deletions

File tree

coroio/http/httpd.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,12 @@ class TResponse {
9494
};
9595

9696
struct IRouter {
97-
virtual TFuture<void> HandleRequest(const TRequest& request, TResponse& response) = 0;
97+
virtual TFuture<void> HandleRequest(TRequest& request, TResponse& response) = 0;
9898
};
9999

100100
class THelloWorldRouter : public IRouter {
101101
public:
102-
TFuture<void> HandleRequest(const TRequest& request, TResponse& response) override {
102+
TFuture<void> HandleRequest(TRequest& request, TResponse& response) override {
103103
if (request.Uri().Path() == "/") {
104104
response.SetStatus(200);
105105
response.SetHeader("Content-Type", "text/plain");

coroio/pipe/pipe.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace NNet {
1717

1818
namespace {
1919

20-
[[maybe_unused]] void SetCloseOnExec(int fd) {
20+
void SetCloseOnExec(int fd) {
2121
int flags = fcntl(fd, F_GETFD);
2222
if (flags == -1) {
2323
throw std::runtime_error("fcntl(F_GETFD) failed");
@@ -70,8 +70,18 @@ void TPipe::TPipeLow::Fork() {
7070
close(pipeStdin[1]);
7171
throw std::system_error(code, std::generic_category(), "pipe() failed for stdout");
7272
}
73+
int pipeStderr[2];
74+
if (pipe(pipeStderr) == -1) {
75+
auto code = errno;
76+
close(pipeStdin[0]);
77+
close(pipeStdin[1]);
78+
close(pipeStdout[0]);
79+
close(pipeStdout[1]);
80+
throw std::system_error(code, std::generic_category(), "pipe() failed for stderr");
81+
}
7382
SetCloseOnExec(pipeStdin[0]); SetCloseOnExec(pipeStdin[1]);
7483
SetCloseOnExec(pipeStdout[0]); SetCloseOnExec(pipeStdout[1]);
84+
SetCloseOnExec(pipeStderr[0]); SetCloseOnExec(pipeStderr[1]);
7585

7686
auto pid = fork();
7787
if (pid == -1) {
@@ -80,13 +90,16 @@ void TPipe::TPipeLow::Fork() {
8090
close(pipeStdin[1]);
8191
close(pipeStdout[0]);
8292
close(pipeStdout[1]);
93+
close(pipeStderr[0]);
94+
close(pipeStderr[1]);
8395
throw std::system_error(code, std::generic_category(), "fork() failed");
8496
}
8597

8698
if (pid == 0) {
8799
// Child process
88100
close(pipeStdin[1]);
89101
close(pipeStdout[0]);
102+
close(pipeStderr[0]);
90103

91104
if (dup2(pipeStdin[0], STDIN_FILENO) == -1) {
92105
std::cerr << "dup2() failed for stdin: " << strerror(errno) << std::endl;
@@ -96,9 +109,14 @@ void TPipe::TPipeLow::Fork() {
96109
std::cerr << "dup2() failed for stdout: " << strerror(errno) << std::endl;
97110
_exit(1);
98111
}
112+
if (dup2(pipeStderr[1], STDERR_FILENO) == -1) {
113+
std::cerr << "dup2() failed for stderr: " << strerror(errno) << std::endl;
114+
_exit(1);
115+
}
99116

100117
close(pipeStdin[0]);
101118
close(pipeStdout[1]);
119+
close(pipeStderr[1]);
102120

103121
std::vector<char*> cargs;
104122
cargs.reserve(Args.size() + 2);
@@ -115,19 +133,26 @@ void TPipe::TPipeLow::Fork() {
115133
// Parent process
116134
close(pipeStdin[0]);
117135
close(pipeStdout[1]);
136+
close(pipeStderr[1]);
118137

119138
ChildPid = pid;
120139
WriteFd = pipeStdin[1];
121140
ReadFd = pipeStdout[0];
141+
ErrFd = pipeStderr[0];
122142
SetNonBlocking(ReadFd);
123143
SetNonBlocking(WriteFd);
144+
SetNonBlocking(ErrFd);
124145
}
125146
}
126147

127148
TFuture<ssize_t> TPipe::ReadSome(void* buffer, size_t size) {
128149
co_return co_await ReadHandle->ReadSome(buffer, size);
129150
}
130151

152+
TFuture<ssize_t> TPipe::ReadSomeErr(void* buffer, size_t size) {
153+
co_return co_await ErrHandle->ReadSome(buffer, size);
154+
}
155+
131156
TFuture<ssize_t> TPipe::WriteSome(const void* buffer, size_t size) {
132157
co_return co_await WriteHandle->WriteSome(buffer, size);
133158
}

coroio/pipe/pipe.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,33 @@ class TPipe {
1616

1717
ReadHandle = std::make_unique<TPipeFileHandle<TPoller>>(PipeLow.ReadFd, poller);
1818
WriteHandle = std::make_unique<TPipeFileHandle<TPoller>>(PipeLow.WriteFd, poller);
19+
ErrHandle = std::make_unique<TPipeFileHandle<TPoller>>(PipeLow.ErrFd, poller);
20+
}
21+
22+
void CloseRead() {
23+
ReadHandle.reset();
24+
}
25+
26+
void CloseWrite() {
27+
WriteHandle.reset();
28+
}
29+
30+
void CloseErr() {
31+
ErrHandle.reset();
32+
}
33+
34+
int Wait() {
35+
int status;
36+
waitpid(PipeLow.ChildPid, &status, 0);
37+
if (WIFEXITED(status)) {
38+
return WEXITSTATUS(status);
39+
} else {
40+
return -1;
41+
}
1942
}
2043

2144
TFuture<ssize_t> ReadSome(void* buffer, size_t size);
45+
TFuture<ssize_t> ReadSomeErr(void* buffer, size_t size);
2246
TFuture<ssize_t> WriteSome(const void* buffer, size_t size);
2347

2448
private:
@@ -33,6 +57,7 @@ class TPipe {
3357

3458
int ReadFd = -1;
3559
int WriteFd = -1;
60+
int ErrFd = -1;
3661
int ChildPid = -1;
3762
};
3863

@@ -61,6 +86,7 @@ class TPipe {
6186
TPipeLow PipeLow;
6287
std::unique_ptr<TTypelessFileHandle> ReadHandle;
6388
std::unique_ptr<TTypelessFileHandle> WriteHandle;
89+
std::unique_ptr<TTypelessFileHandle> ErrHandle;
6490
};
6591

6692
#endif // _WIN32

tests/test_pipe.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,32 @@ void test_pipe_basic_read_write(void**) {
5555
assert_true(bytesRead == sizeof(testData));
5656
assert_true(memcmp(buffer, testData, sizeof(testData)) == 0);
5757
}
58+
59+
void test_pipe_read_stderr(void**) {
60+
TLoop<TDefaultPoller> loop;
61+
auto& poller = loop.Poller();
62+
std::string bashPath = "/bin/bash";
63+
TPipe pipe(poller, bashPath, {"-c", "echo 'error message' 1>&2"});
64+
65+
char buffer[64] = {};
66+
size_t bytesRead = sizeof(buffer);
67+
auto reader = [](TPipe& pipe, char* buffer, size_t& size) -> TFuture<void> {
68+
try {
69+
size = co_await pipe.ReadSomeErr(buffer, size);
70+
} catch (const std::exception& ex) {
71+
std::cerr << "Read error: " << ex.what() << "\n";
72+
}
73+
}(pipe, buffer, bytesRead);
74+
75+
while (!reader.done()) {
76+
loop.Step();
77+
}
78+
79+
const char expected[] = "error message\n";
80+
assert_true(bytesRead == sizeof(expected)-1);
81+
assert_true(memcmp(buffer, expected, sizeof(expected)-1) == 0);
82+
}
83+
5884
#endif // _WIN32
5985

6086
int main(int argc, char** argv) {
@@ -68,6 +94,7 @@ int main(int argc, char** argv) {
6894

6995
#ifndef _WIN32
7096
ADD_TEST(cmocka_unit_test, test_pipe_basic_read_write);
97+
ADD_TEST(cmocka_unit_test, test_pipe_read_stderr);
7198
#endif
7299

73100
return _cmocka_run_group_tests("test_pipe", tests.data(), tests.size(), NULL, NULL);

0 commit comments

Comments
 (0)