nixd
Loading...
Searching...
No Matches
ForkPiped.cpp
Go to the documentation of this file.
2
3#include "ForkPipedPlatform.h"
4
5#include <cerrno>
6#include <fcntl.h>
7
8#include <system_error>
9#include <unistd.h>
10#include <utility>
11
12namespace {
13
14// Own every endpoint until it is transferred to the parent. This object is
15// constructed before pipe creation so partial creation failures are covered.
16struct PipeDescriptors {
17 int In[2] = {-1, -1};
18 int Out[2] = {-1, -1};
19 int Err[2] = {-1, -1};
20
21 PipeDescriptors() = default;
22 PipeDescriptors(const PipeDescriptors &) = delete;
23 PipeDescriptors &operator=(const PipeDescriptors &) = delete;
24
25 ~PipeDescriptors() {
26 for (const auto *Pipe : {In, Out, Err})
27 for (int I = 0; I < 2; ++I)
28 if (Pipe[I] != -1)
29 close(Pipe[I]);
30 }
31};
32
33} // namespace
34
35int nixd::forkPiped(int &In, int &Out, int &Err) {
36 detail::PipeForkScope Platform;
37 static constexpr int READ = 0;
38 static constexpr int WRITE = 1;
39 PipeDescriptors Pipes;
40 auto &PipeIn = Pipes.In;
41 auto &PipeOut = Pipes.Out;
42 auto &PipeErr = Pipes.Err;
43 if (Platform.createPipe(PipeIn) == -1 || Platform.createPipe(PipeOut) == -1 ||
44 Platform.createPipe(PipeErr) == -1)
45 throw std::system_error(errno, std::generic_category());
46
47 // Keep the sources away from stdio, even if the caller closed fd 0, 1 or 2.
48 // This also ensures dup2 clears CLOEXEC rather than becoming a no-op.
49 for (auto *Pipe : {PipeIn, PipeOut, PipeErr})
50 for (int I = 0; I < 2; ++I)
51 if (Pipe[I] <= STDERR_FILENO) {
52 int FD = fcntl(Pipe[I], F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
53 if (FD == -1)
54 throw std::system_error(errno, std::generic_category());
55 close(Pipe[I]);
56 Pipe[I] = FD;
57 }
58
59 pid_t Child = Platform.forkProcess();
60 if (Child == -1)
61 throw std::system_error(errno, std::generic_category());
62
63 if (Child == 0) {
64 // Redirect stdin, stdout, stderr.
65 const int Sources[] = {PipeIn[READ], PipeOut[WRITE], PipeErr[WRITE]};
66 for (int Target = STDIN_FILENO; Target <= STDERR_FILENO; ++Target) {
67 int Result;
68 do {
69 Result = dup2(Sources[Target], Target);
70 } while (Result == -1 && errno == EINTR);
71 if (Result == -1)
72 _exit(127);
73 }
74 return 0;
75 }
76
77 In = std::exchange(PipeIn[WRITE], -1);
78 Out = std::exchange(PipeOut[READ], -1);
79 Err = std::exchange(PipeErr[READ], -1);
80 return Child;
81}
int createPipe(int Pipe[2]) const
int forkPiped(int &In, int &Out, int &Err)
fork this process and create some pipes connected to the new process.
Definition ForkPiped.cpp:35