ian@0
|
1 //
|
ian@0
|
2 // Boost.Process
|
ian@0
|
3 // ~~~~~~~~~~~~~
|
ian@0
|
4 //
|
ian@0
|
5 // Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
ian@0
|
6 // Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
|
ian@0
|
7 // Copyright (c) 2009 Boris Schaeling
|
ian@0
|
8 // Copyright (c) 2010 Felipe Tanus, Boris Schaeling
|
ian@0
|
9 //
|
ian@0
|
10 // Distributed under the Boost Software License, Version 1.0. (See accompanying
|
ian@0
|
11 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
ian@0
|
12 //
|
ian@0
|
13
|
ian@0
|
14 /**
|
ian@0
|
15 * \file boost/process/stream_behavior.hpp
|
ian@0
|
16 *
|
ian@0
|
17 * Includes the declaration of stream behavior classes.
|
ian@0
|
18 */
|
ian@0
|
19
|
ian@0
|
20 #ifndef BOOST_PROCESS_STREAM_BEHAVIOR_HPP
|
ian@0
|
21 #define BOOST_PROCESS_STREAM_BEHAVIOR_HPP
|
ian@0
|
22
|
ian@0
|
23 #include <boost/process/config.hpp>
|
ian@0
|
24
|
ian@0
|
25 #if defined(BOOST_POSIX_API)
|
ian@0
|
26 # include <sys/stat.h>
|
ian@0
|
27 # include <fcntl.h>
|
ian@0
|
28 # include <unistd.h>
|
ian@0
|
29 #elif defined(BOOST_WINDOWS_API)
|
ian@0
|
30 # include <windows.h>
|
ian@0
|
31 # include <rpc.h>
|
ian@0
|
32 #endif
|
ian@0
|
33
|
ian@0
|
34 #include <boost/process/stream_ends.hpp>
|
ian@0
|
35 #include <boost/process/stream_type.hpp>
|
ian@0
|
36 #include <boost/process/handle.hpp>
|
ian@0
|
37 #include <string>
|
ian@0
|
38 #include <algorithm>
|
ian@0
|
39
|
ian@0
|
40 namespace boost {
|
ian@0
|
41 namespace process {
|
ian@0
|
42 namespace behavior {
|
ian@0
|
43
|
ian@0
|
44 /**
|
ian@0
|
45 * Stream behavior to close streams of a child process.
|
ian@0
|
46 *
|
ian@0
|
47 * A child process will not be able to use the stream.
|
ian@0
|
48 */
|
ian@0
|
49 class close
|
ian@0
|
50 {
|
ian@0
|
51 public:
|
ian@0
|
52 stream_ends operator()(stream_type) const
|
ian@0
|
53 {
|
ian@0
|
54 return stream_ends();
|
ian@0
|
55 }
|
ian@0
|
56 };
|
ian@0
|
57
|
ian@0
|
58 /**
|
ian@0
|
59 * Stream behavior to make a child process inherit streams.
|
ian@0
|
60 *
|
ian@0
|
61 * A child process will use the very same stream of its parent process.
|
ian@0
|
62 */
|
ian@0
|
63 class inherit
|
ian@0
|
64 {
|
ian@0
|
65 public:
|
ian@0
|
66 inherit(handle::native_type h)
|
ian@0
|
67 : h_(h, handle::dont_close)
|
ian@0
|
68 {
|
ian@0
|
69 #if defined(BOOST_WINDOWS_API)
|
ian@0
|
70 if (h != INVALID_HANDLE_VALUE)
|
ian@0
|
71 {
|
ian@0
|
72 if (!SetHandleInformation(h_.native(), HANDLE_FLAG_INHERIT,
|
ian@0
|
73 HANDLE_FLAG_INHERIT))
|
ian@0
|
74 {
|
ian@0
|
75 HANDLE proc = GetCurrentProcess();
|
ian@0
|
76 HANDLE dup;
|
ian@0
|
77 if (!DuplicateHandle(proc, h_.native(), proc, &dup, 0,
|
ian@0
|
78 TRUE, DUPLICATE_SAME_ACCESS))
|
ian@0
|
79 {
|
ian@0
|
80 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
|
ian@0
|
81 "DuplicateHandle() failed");
|
ian@0
|
82 }
|
ian@0
|
83 h_ = dup;
|
ian@0
|
84 }
|
ian@0
|
85 }
|
ian@0
|
86 #endif
|
ian@0
|
87 }
|
ian@0
|
88
|
ian@0
|
89 stream_ends operator()(stream_type) const
|
ian@0
|
90 {
|
ian@0
|
91 return stream_ends(h_, handle());
|
ian@0
|
92 }
|
ian@0
|
93
|
ian@0
|
94 private:
|
ian@0
|
95 handle h_;
|
ian@0
|
96 };
|
ian@0
|
97
|
ian@0
|
98 /**
|
ian@0
|
99 * Stream behavior to redirect streams with a pipe.
|
ian@0
|
100 *
|
ian@0
|
101 * A child process will be able to communicate with its parent process.
|
ian@0
|
102 */
|
ian@0
|
103 class pipe
|
ian@0
|
104 {
|
ian@0
|
105 public:
|
ian@0
|
106 #if defined(BOOST_POSIX_API)
|
ian@0
|
107 pipe()
|
ian@0
|
108 : stype_(unknown_stream)
|
ian@0
|
109 {
|
ian@0
|
110 }
|
ian@0
|
111
|
ian@0
|
112 pipe(stream_type stype)
|
ian@0
|
113 : stype_(stype)
|
ian@0
|
114 {
|
ian@0
|
115 }
|
ian@0
|
116 #endif
|
ian@0
|
117
|
ian@0
|
118 stream_ends operator()(stream_type stype) const
|
ian@0
|
119 {
|
ian@0
|
120 handle::native_type ends[2];
|
ian@0
|
121 #if defined(BOOST_POSIX_API)
|
ian@0
|
122 if (::pipe(ends) == -1)
|
ian@0
|
123 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("pipe(2) failed");
|
ian@0
|
124 if (stype_ != unknown_stream)
|
ian@0
|
125 stype = stype_;
|
ian@0
|
126 #elif defined(BOOST_WINDOWS_API)
|
ian@0
|
127 SECURITY_ATTRIBUTES sa;
|
ian@0
|
128 ZeroMemory(&sa, sizeof(sa));
|
ian@0
|
129 sa.nLength = sizeof(sa);
|
ian@0
|
130 sa.lpSecurityDescriptor = NULL;
|
ian@0
|
131 sa.bInheritHandle = FALSE;
|
ian@0
|
132 if (!CreatePipe(&ends[0], &ends[1], &sa, 0))
|
ian@0
|
133 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreatePipe() failed");
|
ian@0
|
134 #endif
|
ian@0
|
135 handle child_end = ends[stype == input_stream ? 0 : 1];
|
ian@0
|
136 handle parent_end = ends[stype == input_stream ? 1 : 0];
|
ian@0
|
137 #if defined(BOOST_WINDOWS_API)
|
ian@0
|
138 if (!SetHandleInformation(child_end.native(), HANDLE_FLAG_INHERIT,
|
ian@0
|
139 HANDLE_FLAG_INHERIT))
|
ian@0
|
140 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
|
ian@0
|
141 "SetHandleInformation() failed");
|
ian@0
|
142 #endif
|
ian@0
|
143 return stream_ends(child_end, parent_end);
|
ian@0
|
144 }
|
ian@0
|
145
|
ian@0
|
146 #if defined(BOOST_POSIX_API)
|
ian@0
|
147 private:
|
ian@0
|
148 stream_type stype_;
|
ian@0
|
149 #endif
|
ian@0
|
150 };
|
ian@0
|
151
|
ian@0
|
152 /**
|
ian@0
|
153 * Stream behavior to redirect streams with a named pipe.
|
ian@0
|
154 *
|
ian@0
|
155 * A child process will be able to communicate with its parent process.
|
ian@0
|
156 */
|
ian@0
|
157 class named_pipe
|
ian@0
|
158 {
|
ian@0
|
159 public:
|
ian@0
|
160 named_pipe(const std::string &name)
|
ian@0
|
161 : name_(name)
|
ian@0
|
162 #if defined(BOOST_POSIX_API)
|
ian@0
|
163 , stype_(unknown_stream)
|
ian@0
|
164 #endif
|
ian@0
|
165 {
|
ian@0
|
166 }
|
ian@0
|
167
|
ian@0
|
168 #if defined(BOOST_POSIX_API)
|
ian@0
|
169 named_pipe(const std::string &name, stream_type stype)
|
ian@0
|
170 : name_(name),
|
ian@0
|
171 stype_(stype)
|
ian@0
|
172 {
|
ian@0
|
173 }
|
ian@0
|
174 #endif
|
ian@0
|
175
|
ian@0
|
176 stream_ends operator()(stream_type stype) const
|
ian@0
|
177 {
|
ian@0
|
178 #if defined(BOOST_POSIX_API)
|
ian@0
|
179 if (mkfifo(name_.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1)
|
ian@0
|
180 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("mkfifo(3) failed");
|
ian@0
|
181 handle child_end = open(name_.c_str(), O_RDONLY | O_NONBLOCK);
|
ian@0
|
182 if (!child_end.valid())
|
ian@0
|
183 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("open(2) failed");
|
ian@0
|
184 int opts = fcntl(child_end.native(), F_GETFL);
|
ian@0
|
185 if (opts == -1)
|
ian@0
|
186 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fcntl(2) failed");
|
ian@0
|
187 opts ^= O_NONBLOCK;
|
ian@0
|
188 if (fcntl(child_end.native(), F_SETFL, opts) == -1)
|
ian@0
|
189 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fcntl(2) failed");
|
ian@0
|
190 handle parent_end = open(name_.c_str(), O_WRONLY);
|
ian@0
|
191 if (!parent_end.valid())
|
ian@0
|
192 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("open(2) failed");
|
ian@0
|
193 if (stype_ != unknown_stream)
|
ian@0
|
194 stype = stype_;
|
ian@0
|
195 #elif defined(BOOST_WINDOWS_API)
|
ian@0
|
196 SECURITY_ATTRIBUTES sa;
|
ian@0
|
197 ZeroMemory(&sa, sizeof(sa));
|
ian@0
|
198 sa.nLength = sizeof(sa);
|
ian@0
|
199 sa.lpSecurityDescriptor = NULL;
|
ian@0
|
200 sa.bInheritHandle = TRUE;
|
ian@0
|
201 handle child_end = CreateNamedPipeA(name_.c_str(), PIPE_ACCESS_INBOUND |
|
ian@0
|
202 FILE_FLAG_OVERLAPPED, 0, 1, 8192, 8192, 0, &sa);
|
ian@0
|
203 if (!child_end.valid())
|
ian@0
|
204 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateNamedPipe() failed");
|
ian@0
|
205 handle parent_end = CreateFileA(name_.c_str(), GENERIC_WRITE, 0, NULL,
|
ian@0
|
206 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
ian@0
|
207 if (!parent_end.valid())
|
ian@0
|
208 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateFile() failed");
|
ian@0
|
209 #endif
|
ian@0
|
210 if (stype == output_stream)
|
ian@0
|
211 std::swap(child_end, parent_end);
|
ian@0
|
212 #if defined(BOOST_WINDOWS_API)
|
ian@0
|
213 if (!SetHandleInformation(child_end.native(), HANDLE_FLAG_INHERIT,
|
ian@0
|
214 HANDLE_FLAG_INHERIT))
|
ian@0
|
215 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
|
ian@0
|
216 "SetHandleInformation() failed");
|
ian@0
|
217 #endif
|
ian@0
|
218 return stream_ends(child_end, parent_end);
|
ian@0
|
219 }
|
ian@0
|
220
|
ian@0
|
221 private:
|
ian@0
|
222 std::string name_;
|
ian@0
|
223
|
ian@0
|
224 #if defined(BOOST_POSIX_API)
|
ian@0
|
225 stream_type stype_;
|
ian@0
|
226 #endif
|
ian@0
|
227 };
|
ian@0
|
228
|
ian@0
|
229 /**
|
ian@0
|
230 * Stream behavior to redirect streams with a pipe which supports asynchronous
|
ian@0
|
231 * I/O.
|
ian@0
|
232 *
|
ian@0
|
233 * As platforms require different types of pipes for asynchronous I/O this
|
ian@0
|
234 * stream behavior is provided for convenience. It uses the minimum required
|
ian@0
|
235 * pipe type on a platform in order to be able to use asynchronous I/O.
|
ian@0
|
236 */
|
ian@0
|
237 #if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN)
|
ian@0
|
238 typedef pipe async_pipe;
|
ian@0
|
239 #elif defined(BOOST_WINDOWS_API)
|
ian@0
|
240 class async_pipe
|
ian@0
|
241 {
|
ian@0
|
242 public:
|
ian@0
|
243 stream_ends operator()(stream_type stype) const
|
ian@0
|
244 {
|
ian@0
|
245 UUID uuid;
|
ian@0
|
246 RPC_STATUS s = UuidCreateSequential(&uuid);
|
ian@0
|
247 if (s != RPC_S_OK && s != RPC_S_UUID_LOCAL_ONLY)
|
ian@0
|
248 BOOST_PROCESS_THROW_ERROR(s, "UuidCreateSequential() failed");
|
ian@0
|
249 unsigned char *c;
|
ian@0
|
250 s = UuidToStringA(&uuid, &c);
|
ian@0
|
251 if (s != RPC_S_OK)
|
ian@0
|
252 BOOST_PROCESS_THROW_ERROR(s, "UuidToString() failed");
|
ian@0
|
253 std::string name;
|
ian@0
|
254 try
|
ian@0
|
255 {
|
ian@0
|
256 name = reinterpret_cast<char*>(c);
|
ian@0
|
257 }
|
ian@0
|
258 catch (...)
|
ian@0
|
259 {
|
ian@0
|
260 RpcStringFreeA(&c);
|
ian@0
|
261 throw;
|
ian@0
|
262 }
|
ian@0
|
263 RpcStringFreeA(&c);
|
ian@0
|
264 named_pipe p("\\\\.\\pipe\\boost_process_" + name);
|
ian@0
|
265 return p(stype);
|
ian@0
|
266 }
|
ian@0
|
267 };
|
ian@0
|
268 #endif
|
ian@0
|
269
|
ian@0
|
270 /**
|
ian@0
|
271 * Stream behavior to mute streams.
|
ian@0
|
272 *
|
ian@0
|
273 * A child process will be able to use streams. But data written to an
|
ian@0
|
274 * output stream is discarded and data read from an input stream is 0.
|
ian@0
|
275 */
|
ian@0
|
276 class null
|
ian@0
|
277 {
|
ian@0
|
278 public:
|
ian@0
|
279 #if defined(BOOST_POSIX_API)
|
ian@0
|
280 null()
|
ian@0
|
281 : stype_(unknown_stream)
|
ian@0
|
282 {
|
ian@0
|
283 }
|
ian@0
|
284
|
ian@0
|
285 null(stream_type stype)
|
ian@0
|
286 : stype_(stype)
|
ian@0
|
287 {
|
ian@0
|
288 }
|
ian@0
|
289 #endif
|
ian@0
|
290
|
ian@0
|
291 stream_ends operator()(stream_type stype) const
|
ian@0
|
292 {
|
ian@0
|
293 #if defined(BOOST_POSIX_API)
|
ian@0
|
294 if (stype_ != unknown_stream)
|
ian@0
|
295 stype = stype_;
|
ian@0
|
296 std::string filename = (stype == input_stream) ? "/dev/zero" :
|
ian@0
|
297 "/dev/null";
|
ian@0
|
298 int flag = (stype == input_stream) ? O_RDONLY : O_WRONLY;
|
ian@0
|
299 handle child_end = open(filename.c_str(), flag);
|
ian@0
|
300 if (!child_end.valid())
|
ian@0
|
301 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("open(2) failed");
|
ian@0
|
302 #elif defined(BOOST_WINDOWS_API)
|
ian@0
|
303 DWORD access = (stype == input_stream) ? GENERIC_READ : GENERIC_WRITE;
|
ian@0
|
304 handle child_end = CreateFileA("NUL", access, 0, NULL, OPEN_EXISTING,
|
ian@0
|
305 FILE_ATTRIBUTE_NORMAL, NULL);
|
ian@0
|
306 if (!child_end.valid())
|
ian@0
|
307 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateFile() failed");
|
ian@0
|
308 if (!SetHandleInformation(child_end.native(), HANDLE_FLAG_INHERIT,
|
ian@0
|
309 HANDLE_FLAG_INHERIT))
|
ian@0
|
310 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
|
ian@0
|
311 "SetHandleInformation() failed");
|
ian@0
|
312 #endif
|
ian@0
|
313 return stream_ends(child_end, handle());
|
ian@0
|
314 }
|
ian@0
|
315
|
ian@0
|
316 #if defined(BOOST_POSIX_API)
|
ian@0
|
317 private:
|
ian@0
|
318 stream_type stype_;
|
ian@0
|
319 #endif
|
ian@0
|
320 };
|
ian@0
|
321
|
ian@0
|
322 }
|
ian@0
|
323 }
|
ian@0
|
324 }
|
ian@0
|
325
|
ian@0
|
326 #endif
|