-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPipe.cpp
173 lines (154 loc) · 4.77 KB
/
Pipe.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include "Pipe.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iterator>
#include <algorithm>
void WriteStdout(FILE *f)
{
while (true)
{
std::vector<char> buf(1024, '\0');
if (!fgets(buf.data(), buf.size(), f))
{
break;
}
if (fprintf(stdout, "%s", buf.data()) < 0)
{
throw std::runtime_error("fprintf error");
}
}
}
void Redirect(FILE *f, const std::filesystem::path &path)
{
FILE *outfile = fopen(path.c_str(), "w");
if (!outfile)
{
const std::string err = "fopen error, " + std::string(std::strerror(errno));
throw std::runtime_error(err);
}
while (true)
{
std::vector<char> buf(1024, '\0');
if (!fgets(buf.data(), buf.size(), f))
{
break;
}
if (fprintf(outfile, "%s", buf.data()) < 0)
{
fclose(outfile);
throw std::runtime_error("fprintf error");
}
}
fclose(outfile);
}
FILE *PipeCommand(FILE *begin, const Job &job)
{
const int beginfd = fileno(begin);
if (beginfd == -1)
{
const std::string err = "fileno error, " + std::string(std::strerror(errno));
throw std::runtime_error(err);
}
int pipelinkfd = beginfd;
for (const auto &command : job.commands)
{
// NOTE: パイプには lseek できない
int pipes[2];
if (pipe(pipes) == -1)
{
const std::string err = "pipe error, " + std::string(std::strerror(errno));
close(pipelinkfd);
throw std::runtime_error(err);
}
const int rfd = pipes[0];
const int wfd = pipes[1];
const auto forked = fork();
if (forked == -1)
{
const std::string err = "fork error, " + std::string(std::strerror(errno));
close(pipelinkfd);
close(rfd);
close(wfd);
throw std::runtime_error(err);
}
// child
if (forked == 0)
{
if (dup2(pipelinkfd, STDIN_FILENO) == -1)
{
const std::string err = "infd dup2 error, " + std::string(std::strerror(errno));
close(pipelinkfd);
close(rfd);
close(wfd);
throw std::runtime_error(err);
}
if (dup2(wfd, STDOUT_FILENO) == -1)
{
const std::string err = "outfd dup2 error, " + std::string(std::strerror(errno));
close(pipelinkfd);
close(rfd);
close(wfd);
throw std::runtime_error(err);
}
std::vector<std::string> args = command.args;
std::vector<char *> ptrs;
std::for_each(args.begin(), args.end(), [&ptrs](auto &arg) { ptrs.push_back(arg.data()); });
// execv は最後の要素に nullptr が必要
ptrs.push_back(nullptr);
// execv が成功すればそのプロセスに置き換わる
execv(args.front().c_str(), ptrs.data());
// プロセスが置き換わらなかったのでエラー
const std::string err = "execv error, " + std::string(std::strerror(errno));
close(pipelinkfd);
close(rfd);
close(wfd);
throw std::runtime_error(err);
}
// parent
{
close(wfd);
int status = 0;
if (wait(&status) == -1)
{
const std::string err = "wait error, " + std::string(std::strerror(errno));
close(pipelinkfd);
close(rfd);
close(wfd);
throw std::runtime_error(err);
}
// プロセスが終了した
if (WIFEXITED(status))
{
if (pipelinkfd != beginfd)
{
close(pipelinkfd);
}
// 今回実行したコマンドの標準出力を取っておき
// 次のコマンドの標準入力を接続する
pipelinkfd = rfd;
}
// シグナルにより終了した
if (WIFSIGNALED(status))
{
const std::string err = "error WIFSIGNALED, " + std::to_string(WTERMSIG(status));
close(pipelinkfd);
close(rfd);
close(wfd);
throw std::runtime_error(err);
}
}
}
FILE *f = fdopen(pipelinkfd, "r");
if (!f)
{
const std::string err = "fdopen error, " + std::string(std::strerror(errno));
throw std::runtime_error(err);
}
return f;
}