/tmp/bitcoin/src/util/subprocess.h
Line | Count | Source |
1 | | // Based on the https://github.com/arun11299/cpp-subprocess project. |
2 | | |
3 | | /*! |
4 | | |
5 | | Documentation for C++ subprocessing library. |
6 | | |
7 | | @copyright The code is licensed under the [MIT |
8 | | License](http://opensource.org/licenses/MIT): |
9 | | <br> |
10 | | Copyright © 2016-2018 Arun Muralidharan. |
11 | | <br> |
12 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
13 | | of this software and associated documentation files (the "Software"), to deal |
14 | | in the Software without restriction, including without limitation the rights |
15 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
16 | | copies of the Software, and to permit persons to whom the Software is |
17 | | furnished to do so, subject to the following conditions: |
18 | | <br> |
19 | | The above copyright notice and this permission notice shall be included in |
20 | | all copies or substantial portions of the Software. |
21 | | <br> |
22 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
23 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
25 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
26 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
27 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
28 | | SOFTWARE. |
29 | | |
30 | | @author [Arun Muralidharan] |
31 | | @see https://github.com/arun11299/cpp-subprocess to download the source code |
32 | | |
33 | | @version 1.0.0 |
34 | | */ |
35 | | |
36 | | #ifndef BITCOIN_UTIL_SUBPROCESS_H |
37 | | #define BITCOIN_UTIL_SUBPROCESS_H |
38 | | |
39 | | #include <util/check.h> |
40 | | #include <util/syserror.h> |
41 | | |
42 | | #include <algorithm> |
43 | | #include <csignal> |
44 | | #include <cstdio> |
45 | | #include <cstdlib> |
46 | | #include <cstring> |
47 | | #include <exception> |
48 | | #include <future> |
49 | | #include <initializer_list> |
50 | | #include <iostream> |
51 | | #include <map> |
52 | | #include <memory> |
53 | | #include <sstream> |
54 | | #include <string> |
55 | | #include <vector> |
56 | | |
57 | | extern "C" { |
58 | | #ifdef WIN32 |
59 | | #include <windows.h> |
60 | | #include <io.h> |
61 | | #else |
62 | | #include <sys/wait.h> |
63 | | #include <unistd.h> |
64 | | #endif |
65 | | #include <csignal> |
66 | | #include <fcntl.h> |
67 | | #include <sys/types.h> |
68 | | } |
69 | | |
70 | | // The Microsoft C++ compiler issues deprecation warnings |
71 | | // for the standard POSIX function names. |
72 | | // Its preferred implementations have a leading underscore. |
73 | | // See: https://learn.microsoft.com/en-us/cpp/c-runtime-library/compatibility. |
74 | | #if (defined _MSC_VER) |
75 | | #define subprocess_close _close |
76 | | #define subprocess_fileno _fileno |
77 | | #define subprocess_open _open |
78 | | #define subprocess_write _write |
79 | | #else |
80 | 166 | #define subprocess_close close |
81 | 116 | #define subprocess_fileno fileno |
82 | | #define subprocess_open open |
83 | 0 | #define subprocess_write write |
84 | | #endif |
85 | | |
86 | | /*! |
87 | | * Getting started with reading this source code. |
88 | | * The source is mainly divided into four parts: |
89 | | * 1. Exception Classes: |
90 | | * These are very basic exception classes derived from |
91 | | * runtime_error exception. |
92 | | * There are two types of exception thrown from subprocess |
93 | | * library: OSError and CalledProcessError |
94 | | * |
95 | | * 2. Popen Class |
96 | | * This is the main class the users will deal with. It |
97 | | * provides with all the API's to deal with processes. |
98 | | * |
99 | | * 3. Util namespace |
100 | | * It includes some helper functions to split/join a string, |
101 | | * reading from file descriptors, waiting on a process, fcntl |
102 | | * options on file descriptors etc. |
103 | | * |
104 | | * 4. Detail namespace |
105 | | * This includes some metaprogramming and helper classes. |
106 | | */ |
107 | | |
108 | | |
109 | | namespace subprocess { |
110 | | |
111 | | // Max buffer size allocated on stack for read error |
112 | | // from pipe |
113 | | inline constexpr size_t SP_MAX_ERR_BUF_SIZ = 1024; |
114 | | |
115 | | // Default buffer capacity for OutBuffer and ErrBuffer. |
116 | | // If the data exceeds this capacity, the buffer size is grown |
117 | | // by 1.5 times its previous capacity |
118 | | inline constexpr size_t DEFAULT_BUF_CAP_BYTES = 8192; |
119 | | |
120 | | |
121 | | /*----------------------------------------------- |
122 | | * EXCEPTION CLASSES |
123 | | *----------------------------------------------- |
124 | | */ |
125 | | |
126 | | /*! |
127 | | * class: CalledProcessError |
128 | | * Thrown when there was error executing the command. |
129 | | * Check Popen class API's to know when this exception |
130 | | * can be thrown. |
131 | | * |
132 | | */ |
133 | | class CalledProcessError: public std::runtime_error |
134 | | { |
135 | | public: |
136 | | int retcode; |
137 | | CalledProcessError(const std::string& error_msg, int retcode): |
138 | 2 | std::runtime_error(error_msg), retcode(retcode) |
139 | 2 | {} |
140 | | }; |
141 | | |
142 | | |
143 | | /*! |
144 | | * class: OSError |
145 | | * Thrown when some system call fails to execute or give result. |
146 | | * The exception message contains the name of the failed system call |
147 | | * with the stringisized errno code. |
148 | | * Check Popen class API's to know when this exception would be |
149 | | * thrown. |
150 | | * Its usual that the API exception specification would have |
151 | | * this exception together with CalledProcessError. |
152 | | */ |
153 | | class OSError: public std::runtime_error |
154 | | { |
155 | | public: |
156 | | OSError(const std::string& err_msg, int err_code): |
157 | 0 | std::runtime_error(err_msg + ": " + SysErrorString(err_code)) |
158 | 0 | {} |
159 | | }; |
160 | | |
161 | | //-------------------------------------------------------------------- |
162 | | namespace util |
163 | | { |
164 | | #ifdef WIN32 |
165 | | inline void quote_argument(const std::string &argument, std::string &command_line, |
166 | | bool force) |
167 | | { |
168 | | constexpr char quote = '"'; |
169 | | constexpr char backslash = '\\'; |
170 | | |
171 | | // |
172 | | // Unless we're told otherwise, don't quote unless we actually |
173 | | // need to do so --- hopefully avoid problems if programs won't |
174 | | // parse quotes properly |
175 | | // |
176 | | |
177 | | if (force == false && argument.empty() == false && |
178 | | argument.find_first_of(" \t\n\v") == argument.npos) { |
179 | | command_line.append(argument); |
180 | | } |
181 | | else { |
182 | | command_line.push_back(quote); |
183 | | |
184 | | for (auto it = argument.begin();; ++it) { |
185 | | unsigned number_backslashes = 0; |
186 | | |
187 | | while (it != argument.end() && *it == backslash) { |
188 | | ++it; |
189 | | ++number_backslashes; |
190 | | } |
191 | | |
192 | | if (it == argument.end()) { |
193 | | |
194 | | // |
195 | | // Escape all backslashes, but let the terminating |
196 | | // double quotation mark we add below be interpreted |
197 | | // as a metacharacter. |
198 | | // |
199 | | |
200 | | command_line.append(number_backslashes * 2, backslash); |
201 | | break; |
202 | | } |
203 | | else if (*it == quote) { |
204 | | |
205 | | // |
206 | | // Escape all backslashes and the following |
207 | | // double quotation mark. |
208 | | // |
209 | | |
210 | | command_line.append(number_backslashes * 2 + 1, backslash); |
211 | | command_line.push_back(*it); |
212 | | } |
213 | | else { |
214 | | |
215 | | // |
216 | | // Backslashes aren't special here. |
217 | | // |
218 | | |
219 | | command_line.append(number_backslashes, backslash); |
220 | | command_line.push_back(*it); |
221 | | } |
222 | | } |
223 | | |
224 | | command_line.push_back(quote); |
225 | | } |
226 | | } |
227 | | |
228 | | inline std::string get_last_error(DWORD errorMessageID) |
229 | | { |
230 | | if (errorMessageID == 0) |
231 | | return std::string(); |
232 | | |
233 | | LPSTR messageBuffer = nullptr; |
234 | | size_t size = FormatMessageA( |
235 | | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
236 | | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, |
237 | | NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
238 | | (LPSTR)&messageBuffer, 0, NULL); |
239 | | |
240 | | std::string message(messageBuffer, size); |
241 | | |
242 | | LocalFree(messageBuffer); |
243 | | |
244 | | return message; |
245 | | } |
246 | | |
247 | | inline FILE *file_from_handle(HANDLE h, const char *mode) |
248 | | { |
249 | | int md; |
250 | | if (!mode) { |
251 | | throw OSError("invalid_mode", 0); |
252 | | } |
253 | | |
254 | | if (mode[0] == 'w') { |
255 | | md = _O_WRONLY; |
256 | | } |
257 | | else if (mode[0] == 'r') { |
258 | | md = _O_RDONLY; |
259 | | } |
260 | | else { |
261 | | throw OSError("file_from_handle", 0); |
262 | | } |
263 | | |
264 | | int os_fhandle = _open_osfhandle((intptr_t)h, md); |
265 | | if (os_fhandle == -1) { |
266 | | CloseHandle(h); |
267 | | throw OSError("_open_osfhandle", 0); |
268 | | } |
269 | | |
270 | | FILE *fp = _fdopen(os_fhandle, mode); |
271 | | if (fp == 0) { |
272 | | subprocess_close(os_fhandle); |
273 | | throw OSError("_fdopen", 0); |
274 | | } |
275 | | |
276 | | return fp; |
277 | | } |
278 | | |
279 | | inline void configure_pipe(HANDLE* read_handle, HANDLE* write_handle, HANDLE* child_handle) |
280 | | { |
281 | | SECURITY_ATTRIBUTES saAttr; |
282 | | |
283 | | // Set the bInheritHandle flag so pipe handles are inherited. |
284 | | saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); |
285 | | saAttr.bInheritHandle = TRUE; |
286 | | saAttr.lpSecurityDescriptor = NULL; |
287 | | |
288 | | // Create a pipe for the child process's STDIN. |
289 | | if (!CreatePipe(read_handle, write_handle, &saAttr,0)) |
290 | | throw OSError("CreatePipe", 0); |
291 | | |
292 | | // Ensure the write handle to the pipe for STDIN is not inherited. |
293 | | if (!SetHandleInformation(*child_handle, HANDLE_FLAG_INHERIT, 0)) |
294 | | throw OSError("SetHandleInformation", 0); |
295 | | } |
296 | | #endif |
297 | | |
298 | | /*! |
299 | | * Function: split |
300 | | * Parameters: |
301 | | * [in] str : Input string which needs to be split based upon the |
302 | | * delimiters provided. |
303 | | * [in] deleims : Delimiter characters based upon which the string needs |
304 | | * to be split. Default constructed to ' '(space) and '\t'(tab) |
305 | | * [out] vector<string> : Vector of strings split at deleimiter. |
306 | | */ |
307 | | static inline std::vector<std::string> |
308 | | split(const std::string& str, const std::string& delims=" \t") |
309 | 39 | { |
310 | 39 | std::vector<std::string> res; |
311 | 39 | size_t init = 0; |
312 | | |
313 | 77 | while (true) { |
314 | 77 | auto pos = str.find_first_of(delims, init); |
315 | 77 | if (pos == std::string::npos) { |
316 | 39 | res.emplace_back(str.substr(init, str.length())); |
317 | 39 | break; |
318 | 39 | } |
319 | 38 | res.emplace_back(str.substr(init, pos - init)); |
320 | 38 | pos++; |
321 | 38 | init = pos; |
322 | 38 | } |
323 | | |
324 | 39 | return res; |
325 | 39 | } Unexecuted instantiation: system_tests.cpp:subprocess::util::split(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) Unexecuted instantiation: run_command.cpp:subprocess::util::split(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) external_signer.cpp:subprocess::util::split(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) Line | Count | Source | 309 | 39 | { | 310 | 39 | std::vector<std::string> res; | 311 | 39 | size_t init = 0; | 312 | | | 313 | 77 | while (true) { | 314 | 77 | auto pos = str.find_first_of(delims, init); | 315 | 77 | if (pos == std::string::npos) { | 316 | 39 | res.emplace_back(str.substr(init, str.length())); | 317 | 39 | break; | 318 | 39 | } | 319 | 38 | res.emplace_back(str.substr(init, pos - init)); | 320 | 38 | pos++; | 321 | 38 | init = pos; | 322 | 38 | } | 323 | | | 324 | 39 | return res; | 325 | 39 | } |
|
326 | | |
327 | | |
328 | | #ifndef WIN32 |
329 | | /*! |
330 | | * Function: set_clo_on_exec |
331 | | * Sets/Resets the FD_CLOEXEC flag on the provided file descriptor |
332 | | * based upon the `set` parameter. |
333 | | * Parameters: |
334 | | * [in] fd : The descriptor on which FD_CLOEXEC needs to be set/reset. |
335 | | * [in] set : If 'true', set FD_CLOEXEC. |
336 | | * If 'false' unset FD_CLOEXEC. |
337 | | */ |
338 | | static inline |
339 | | void set_clo_on_exec(int fd, bool set = true) |
340 | 320 | { |
341 | 320 | int flags = fcntl(fd, F_GETFD, 0); |
342 | 320 | if (flags == -1) { |
343 | 0 | throw OSError("fcntl F_GETFD failed", errno); |
344 | 0 | } |
345 | 320 | if (set) flags |= FD_CLOEXEC; |
346 | 0 | else flags &= ~FD_CLOEXEC; |
347 | 320 | if (fcntl(fd, F_SETFD, flags) == -1) { |
348 | 0 | throw OSError("fcntl F_SETFD failed", errno); |
349 | 0 | } |
350 | 320 | } Unexecuted instantiation: system_tests.cpp:subprocess::util::set_clo_on_exec(int, bool) run_command.cpp:subprocess::util::set_clo_on_exec(int, bool) Line | Count | Source | 340 | 320 | { | 341 | 320 | int flags = fcntl(fd, F_GETFD, 0); | 342 | 320 | if (flags == -1) { | 343 | 0 | throw OSError("fcntl F_GETFD failed", errno); | 344 | 0 | } | 345 | 320 | if (set) flags |= FD_CLOEXEC; | 346 | 0 | else flags &= ~FD_CLOEXEC; | 347 | 320 | if (fcntl(fd, F_SETFD, flags) == -1) { | 348 | | throw OSError("fcntl F_SETFD failed", errno); | 349 | 0 | } | 350 | 320 | } |
Unexecuted instantiation: external_signer.cpp:subprocess::util::set_clo_on_exec(int, bool) |
351 | | |
352 | | |
353 | | /*! |
354 | | * Function: pipe_cloexec |
355 | | * Creates a pipe and sets FD_CLOEXEC flag on both |
356 | | * read and write descriptors of the pipe. |
357 | | * Parameters: |
358 | | * [out] : A pair of file descriptors. |
359 | | * First element of pair is the read descriptor of pipe. |
360 | | * Second element is the write descriptor of pipe. |
361 | | */ |
362 | | static inline |
363 | | std::pair<int, int> pipe_cloexec() noexcept(false) |
364 | 160 | { |
365 | 160 | int pipe_fds[2]; |
366 | 160 | int res = pipe(pipe_fds); |
367 | 160 | if (res) { |
368 | 0 | throw OSError("pipe failure", errno); |
369 | 0 | } |
370 | | |
371 | 160 | set_clo_on_exec(pipe_fds[0]); |
372 | 160 | set_clo_on_exec(pipe_fds[1]); |
373 | | |
374 | 160 | return std::make_pair(pipe_fds[0], pipe_fds[1]); |
375 | 160 | } Unexecuted instantiation: system_tests.cpp:subprocess::util::pipe_cloexec() run_command.cpp:subprocess::util::pipe_cloexec() Line | Count | Source | 364 | 160 | { | 365 | 160 | int pipe_fds[2]; | 366 | 160 | int res = pipe(pipe_fds); | 367 | 160 | if (res) { | 368 | 0 | throw OSError("pipe failure", errno); | 369 | 0 | } | 370 | | | 371 | 160 | set_clo_on_exec(pipe_fds[0]); | 372 | 160 | set_clo_on_exec(pipe_fds[1]); | 373 | | | 374 | 160 | return std::make_pair(pipe_fds[0], pipe_fds[1]); | 375 | 160 | } |
Unexecuted instantiation: external_signer.cpp:subprocess::util::pipe_cloexec() |
376 | | #endif |
377 | | |
378 | | |
379 | | /*! |
380 | | * Function: write_n |
381 | | * Writes `length` bytes to the file descriptor `fd` |
382 | | * from the buffer `buf`. |
383 | | * Parameters: |
384 | | * [in] fd : The file descriptotr to write to. |
385 | | * [in] buf: Buffer from which data needs to be written to fd. |
386 | | * [in] length: The number of bytes that needs to be written from |
387 | | * `buf` to `fd`. |
388 | | * [out] int : Number of bytes written or -1 in case of failure. |
389 | | */ |
390 | | static inline |
391 | | int write_n(int fd, const char* buf, size_t length) |
392 | 0 | { |
393 | 0 | size_t nwritten = 0; |
394 | 0 | while (nwritten < length) { |
395 | 0 | int written = subprocess_write(fd, buf + nwritten, length - nwritten); |
396 | 0 | if (written == -1) return -1; |
397 | 0 | nwritten += written; |
398 | 0 | } |
399 | 0 | return nwritten; |
400 | 0 | } Unexecuted instantiation: system_tests.cpp:subprocess::util::write_n(int, char const*, unsigned long) Unexecuted instantiation: run_command.cpp:subprocess::util::write_n(int, char const*, unsigned long) Unexecuted instantiation: external_signer.cpp:subprocess::util::write_n(int, char const*, unsigned long) |
401 | | |
402 | | |
403 | | /*! |
404 | | * Function: read_atmost_n |
405 | | * Reads at the most `read_upto` bytes from the |
406 | | * file object `fp` before returning. |
407 | | * Parameters: |
408 | | * [in] fp : The file object from which it needs to read. |
409 | | * [in] buf : The buffer into which it needs to write the data. |
410 | | * [in] read_upto: Max number of bytes which must be read from `fd`. |
411 | | * [out] int : Number of bytes written to `buf` or read from `fd` |
412 | | * OR -1 in case of error. |
413 | | * NOTE: In case of EINTR while reading from socket, this API |
414 | | * will retry to read from `fd`, but only till the EINTR counter |
415 | | * reaches 50 after which it will return with whatever data it read. |
416 | | */ |
417 | | static inline |
418 | | int read_atmost_n(FILE* fp, char* buf, size_t read_upto) |
419 | 116 | { |
420 | | #ifdef WIN32 |
421 | | return (int)fread(buf, 1, read_upto, fp); |
422 | | #else |
423 | 116 | int fd = subprocess_fileno(fp); |
424 | 116 | int rbytes = 0; |
425 | 116 | int eintr_cnter = 0; |
426 | | |
427 | 154 | while (1) { |
428 | 154 | int read_bytes = read(fd, buf + rbytes, read_upto - rbytes); |
429 | 154 | if (read_bytes == -1) { |
430 | 0 | if (errno == EINTR) { |
431 | 0 | if (eintr_cnter >= 50) return -1; |
432 | 0 | eintr_cnter++; |
433 | 0 | continue; |
434 | 0 | } |
435 | 0 | return -1; |
436 | 0 | } |
437 | 154 | if (read_bytes == 0) return rbytes; |
438 | | |
439 | 38 | rbytes += read_bytes; |
440 | 38 | } |
441 | 0 | return rbytes; |
442 | 116 | #endif |
443 | 116 | } Unexecuted instantiation: system_tests.cpp:subprocess::util::read_atmost_n(_IO_FILE*, char*, unsigned long) run_command.cpp:subprocess::util::read_atmost_n(_IO_FILE*, char*, unsigned long) Line | Count | Source | 419 | 116 | { | 420 | | #ifdef WIN32 | 421 | | return (int)fread(buf, 1, read_upto, fp); | 422 | | #else | 423 | 116 | int fd = subprocess_fileno(fp); | 424 | 116 | int rbytes = 0; | 425 | 116 | int eintr_cnter = 0; | 426 | | | 427 | 154 | while (1) { | 428 | 154 | int read_bytes = read(fd, buf + rbytes, read_upto - rbytes); | 429 | 154 | if (read_bytes == -1) { | 430 | 0 | if (errno == EINTR) { | 431 | 0 | if (eintr_cnter >= 50) return -1; | 432 | 0 | eintr_cnter++; | 433 | 0 | continue; | 434 | 0 | } | 435 | 0 | return -1; | 436 | 0 | } | 437 | 154 | if (read_bytes == 0) return rbytes; | 438 | | | 439 | 38 | rbytes += read_bytes; | 440 | 38 | } | 441 | 0 | return rbytes; | 442 | 116 | #endif | 443 | 116 | } |
Unexecuted instantiation: external_signer.cpp:subprocess::util::read_atmost_n(_IO_FILE*, char*, unsigned long) |
444 | | |
445 | | |
446 | | /*! |
447 | | * Function: read_all |
448 | | * Reads all the available data from `fp` into |
449 | | * `buf`. Internally calls read_atmost_n. |
450 | | * Parameters: |
451 | | * [in] fp : The file object from which to read from. |
452 | | * [in] buf : The buffer of type `class Buffer` into which |
453 | | * the read data is written to. |
454 | | * [out] int: Number of bytes read OR -1 in case of failure. |
455 | | * |
456 | | * NOTE: `class Buffer` is a exposed public class. See below. |
457 | | */ |
458 | | |
459 | | static inline int read_all(FILE* fp, std::vector<char>& buf) |
460 | 76 | { |
461 | 76 | auto buffer = buf.data(); |
462 | 76 | int total_bytes_read = 0; |
463 | 76 | int fill_sz = buf.size(); |
464 | | |
465 | 76 | while (1) { |
466 | 76 | const int rd_bytes = read_atmost_n(fp, buffer, fill_sz); |
467 | | |
468 | 76 | if (rd_bytes == -1) { // Read finished |
469 | 0 | if (total_bytes_read == 0) return -1; |
470 | 0 | break; |
471 | |
|
472 | 76 | } else if (rd_bytes == fill_sz) { // Buffer full |
473 | 0 | const auto orig_sz = buf.size(); |
474 | 0 | const auto new_sz = orig_sz * 2; |
475 | 0 | buf.resize(new_sz); |
476 | 0 | fill_sz = new_sz - orig_sz; |
477 | | |
478 | | //update the buffer pointer |
479 | 0 | buffer = buf.data(); |
480 | 0 | total_bytes_read += rd_bytes; |
481 | 0 | buffer += total_bytes_read; |
482 | |
|
483 | 76 | } else { // Partial data ? Continue reading |
484 | 76 | total_bytes_read += rd_bytes; |
485 | 76 | fill_sz -= rd_bytes; |
486 | 76 | break; |
487 | 76 | } |
488 | 76 | } |
489 | 76 | buf.erase(buf.begin()+total_bytes_read, buf.end()); // remove extra nulls |
490 | 76 | return total_bytes_read; |
491 | 76 | } Unexecuted instantiation: system_tests.cpp:subprocess::util::read_all(_IO_FILE*, std::vector<char, std::allocator<char>>&) run_command.cpp:subprocess::util::read_all(_IO_FILE*, std::vector<char, std::allocator<char>>&) Line | Count | Source | 460 | 76 | { | 461 | 76 | auto buffer = buf.data(); | 462 | 76 | int total_bytes_read = 0; | 463 | 76 | int fill_sz = buf.size(); | 464 | | | 465 | 76 | while (1) { | 466 | 76 | const int rd_bytes = read_atmost_n(fp, buffer, fill_sz); | 467 | | | 468 | 76 | if (rd_bytes == -1) { // Read finished | 469 | 0 | if (total_bytes_read == 0) return -1; | 470 | 0 | break; | 471 | |
| 472 | 76 | } else if (rd_bytes == fill_sz) { // Buffer full | 473 | 0 | const auto orig_sz = buf.size(); | 474 | 0 | const auto new_sz = orig_sz * 2; | 475 | 0 | buf.resize(new_sz); | 476 | 0 | fill_sz = new_sz - orig_sz; | 477 | | | 478 | | //update the buffer pointer | 479 | 0 | buffer = buf.data(); | 480 | 0 | total_bytes_read += rd_bytes; | 481 | 0 | buffer += total_bytes_read; | 482 | |
| 483 | 76 | } else { // Partial data ? Continue reading | 484 | 76 | total_bytes_read += rd_bytes; | 485 | 76 | fill_sz -= rd_bytes; | 486 | 76 | break; | 487 | 76 | } | 488 | 76 | } | 489 | 76 | buf.erase(buf.begin()+total_bytes_read, buf.end()); // remove extra nulls | 490 | 76 | return total_bytes_read; | 491 | 76 | } |
Unexecuted instantiation: external_signer.cpp:subprocess::util::read_all(_IO_FILE*, std::vector<char, std::allocator<char>>&) |
492 | | |
493 | | #ifndef WIN32 |
494 | | /*! |
495 | | * Function: wait_for_child_exit |
496 | | * Waits for the process with pid `pid` to exit |
497 | | * and returns its status. |
498 | | * Parameters: |
499 | | * [in] pid : The pid of the process. |
500 | | * [out] pair<int, int>: |
501 | | * pair.first : Return code of the waitpid call. |
502 | | * pair.second : Exit status of the process. |
503 | | * |
504 | | * NOTE: This is a blocking call as in, it will loop |
505 | | * till the child is exited. |
506 | | */ |
507 | | static inline |
508 | | std::pair<int, int> wait_for_child_exit(int pid) |
509 | 40 | { |
510 | 40 | int status = 0; |
511 | 40 | int ret = -1; |
512 | 40 | while (1) { |
513 | 40 | ret = waitpid(pid, &status, 0); |
514 | 40 | if (ret == -1) break; |
515 | 40 | if (ret == 0) continue; |
516 | 40 | return std::make_pair(ret, status); |
517 | 40 | } |
518 | | |
519 | 0 | return std::make_pair(ret, status); |
520 | 40 | } Unexecuted instantiation: system_tests.cpp:subprocess::util::wait_for_child_exit(int) run_command.cpp:subprocess::util::wait_for_child_exit(int) Line | Count | Source | 509 | 40 | { | 510 | 40 | int status = 0; | 511 | 40 | int ret = -1; | 512 | 40 | while (1) { | 513 | 40 | ret = waitpid(pid, &status, 0); | 514 | 40 | if (ret == -1) break; | 515 | 40 | if (ret == 0) continue; | 516 | 40 | return std::make_pair(ret, status); | 517 | 40 | } | 518 | | | 519 | 0 | return std::make_pair(ret, status); | 520 | 40 | } |
Unexecuted instantiation: external_signer.cpp:subprocess::util::wait_for_child_exit(int) |
521 | | #endif |
522 | | |
523 | | } // end namespace util |
524 | | |
525 | | |
526 | | |
527 | | /* ------------------------------- |
528 | | * Popen Arguments |
529 | | * ------------------------------- |
530 | | */ |
531 | | |
532 | | /*! |
533 | | * Base class for all arguments involving string value. |
534 | | */ |
535 | | struct string_arg |
536 | | { |
537 | 0 | string_arg(const char* arg): arg_value(arg) {} |
538 | 0 | string_arg(std::string&& arg): arg_value(std::move(arg)) {} |
539 | 0 | string_arg(const std::string& arg): arg_value(arg) {} |
540 | | std::string arg_value; |
541 | | }; |
542 | | |
543 | | /*! |
544 | | * Option to specify the executable name separately |
545 | | * from the args sequence. |
546 | | * In this case the cmd args must only contain the |
547 | | * options required for this executable. |
548 | | * |
549 | | * Eg: executable{"ls"} |
550 | | */ |
551 | | struct executable: string_arg |
552 | | { |
553 | | template <typename T> |
554 | | executable(T&& arg): string_arg(std::forward<T>(arg)) {} |
555 | | }; |
556 | | |
557 | | /*! |
558 | | * Used for redirecting input/output/error |
559 | | */ |
560 | | enum IOTYPE { |
561 | | STDOUT = 1, |
562 | | STDERR, |
563 | | PIPE, |
564 | | }; |
565 | | |
566 | | //TODO: A common base/interface for below stream structures ?? |
567 | | |
568 | | /*! |
569 | | * Option to specify the input channel for the child |
570 | | * process. It can be: |
571 | | * 1. An already open file descriptor. |
572 | | * 2. A file name. |
573 | | * 3. IOTYPE. Usual a PIPE |
574 | | * |
575 | | * Eg: input{PIPE} |
576 | | * OR in case of redirection, output of another Popen |
577 | | * input{popen.output()} |
578 | | */ |
579 | | struct input |
580 | | { |
581 | | // For an already existing file descriptor. |
582 | 0 | explicit input(int fd): rd_ch_(fd) {} |
583 | | |
584 | | // FILE pointer. |
585 | 0 | explicit input (FILE* fp):input(subprocess_fileno(fp)) { assert(fp); } |
586 | | |
587 | 0 | explicit input(const char* filename) { |
588 | 0 | int fd = subprocess_open(filename, O_RDONLY); |
589 | 0 | if (fd == -1) throw OSError("File not found: ", errno); |
590 | 0 | rd_ch_ = fd; |
591 | 0 | } |
592 | 40 | explicit input(IOTYPE typ) { |
593 | 40 | assert (typ == PIPE && "STDOUT/STDERR not allowed"); |
594 | 40 | #ifndef WIN32 |
595 | 40 | std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec(); |
596 | 40 | #endif |
597 | 40 | } |
598 | | |
599 | | int rd_ch_ = -1; |
600 | | int wr_ch_ = -1; |
601 | | }; |
602 | | |
603 | | |
604 | | /*! |
605 | | * Option to specify the output channel for the child |
606 | | * process. It can be: |
607 | | * 1. An already open file descriptor. |
608 | | * 2. A file name. |
609 | | * 3. IOTYPE. Usually a PIPE. |
610 | | * |
611 | | * Eg: output{PIPE} |
612 | | * OR output{"output.txt"} |
613 | | */ |
614 | | struct output |
615 | | { |
616 | 0 | explicit output(int fd): wr_ch_(fd) {} |
617 | | |
618 | 0 | explicit output (FILE* fp):output(subprocess_fileno(fp)) { assert(fp); } |
619 | | |
620 | 0 | explicit output(const char* filename) { |
621 | 0 | int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640); |
622 | 0 | if (fd == -1) throw OSError("File not found: ", errno); |
623 | 0 | wr_ch_ = fd; |
624 | 0 | } |
625 | 40 | explicit output(IOTYPE typ) { |
626 | 40 | assert (typ == PIPE && "STDOUT/STDERR not allowed"); |
627 | 40 | #ifndef WIN32 |
628 | 40 | std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec(); |
629 | 40 | #endif |
630 | 40 | } |
631 | | |
632 | | int rd_ch_ = -1; |
633 | | int wr_ch_ = -1; |
634 | | }; |
635 | | |
636 | | |
637 | | /*! |
638 | | * Option to specify the error channel for the child |
639 | | * process. It can be: |
640 | | * 1. An already open file descriptor. |
641 | | * 2. A file name. |
642 | | * 3. IOTYPE. Usually a PIPE or STDOUT |
643 | | * |
644 | | */ |
645 | | struct error |
646 | | { |
647 | 0 | explicit error(int fd): wr_ch_(fd) {} |
648 | | |
649 | 0 | explicit error(FILE* fp):error(subprocess_fileno(fp)) { assert(fp); } |
650 | | |
651 | 0 | explicit error(const char* filename) { |
652 | 0 | int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640); |
653 | 0 | if (fd == -1) throw OSError("File not found: ", errno); |
654 | 0 | wr_ch_ = fd; |
655 | 0 | } |
656 | 40 | explicit error(IOTYPE typ) { |
657 | 40 | assert ((typ == PIPE || typ == STDOUT) && "STDERR not allowed"); |
658 | 40 | if (typ == PIPE) { |
659 | 40 | #ifndef WIN32 |
660 | 40 | std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec(); |
661 | 40 | #endif |
662 | 40 | } else { |
663 | | // Need to defer it till we have checked all arguments |
664 | 0 | deferred_ = true; |
665 | 0 | } |
666 | 40 | } |
667 | | |
668 | | bool deferred_ = false; |
669 | | int rd_ch_ = -1; |
670 | | int wr_ch_ = -1; |
671 | | }; |
672 | | |
673 | | // ~~~~ End Popen Args ~~~~ |
674 | | |
675 | | |
676 | | /*! |
677 | | * class: Buffer |
678 | | * This class is a very thin wrapper around std::vector<char> |
679 | | * This is basically used to determine the length of the actual |
680 | | * data stored inside the dynamically resized vector. |
681 | | * |
682 | | * This is what is returned as the output to the communicate |
683 | | * function, so, users must know about this class. |
684 | | * |
685 | | * OutBuffer and ErrBuffer are just different typedefs to this class. |
686 | | */ |
687 | | class Buffer |
688 | | { |
689 | | public: |
690 | 76 | Buffer() = default; |
691 | 0 | explicit Buffer(size_t cap) { buf.resize(cap); } |
692 | 76 | void add_cap(size_t cap) { buf.resize(cap); } |
693 | | |
694 | | public: |
695 | | std::vector<char> buf; |
696 | | size_t length = 0; |
697 | | }; |
698 | | |
699 | | // Buffer for storing output written to output fd |
700 | | using OutBuffer = Buffer; |
701 | | // Buffer for storing output written to error fd |
702 | | using ErrBuffer = Buffer; |
703 | | |
704 | | |
705 | | // Fwd Decl. |
706 | | class Popen; |
707 | | |
708 | | /*--------------------------------------------------- |
709 | | * DETAIL NAMESPACE |
710 | | *--------------------------------------------------- |
711 | | */ |
712 | | |
713 | | namespace detail { |
714 | | /*! |
715 | | * A helper class to Popen class for setting |
716 | | * options as provided in the Popen constructor. |
717 | | * This design allows us to _not_ have any fixed position |
718 | | * to any arguments and specify them in a way similar to what |
719 | | * can be done in python. |
720 | | */ |
721 | | struct ArgumentDeducer |
722 | | { |
723 | 120 | ArgumentDeducer(Popen* p): popen_(p) {} |
724 | | |
725 | | void set_option(executable&& exe); |
726 | | void set_option(input&& inp); |
727 | | void set_option(output&& out); |
728 | | void set_option(error&& err); |
729 | | |
730 | | private: |
731 | | Popen* popen_ = nullptr; |
732 | | }; |
733 | | |
734 | | #ifndef WIN32 |
735 | | /*! |
736 | | * A helper class to Popen. |
737 | | * This takes care of all the fork-exec logic |
738 | | * in the execute_child API. |
739 | | */ |
740 | | class Child |
741 | | { |
742 | | public: |
743 | | Child(Popen* p, int err_wr_pipe): |
744 | 0 | parent_(p), |
745 | 0 | err_wr_pipe_(err_wr_pipe) |
746 | 0 | {} |
747 | | |
748 | | void execute_child(); |
749 | | |
750 | | private: |
751 | | // Lets call it parent even though |
752 | | // technically a bit incorrect |
753 | | Popen* parent_ = nullptr; |
754 | | int err_wr_pipe_ = -1; |
755 | | }; |
756 | | #endif |
757 | | |
758 | | // Fwd Decl. |
759 | | class Streams; |
760 | | |
761 | | /*! |
762 | | * A helper class to Streams. |
763 | | * This takes care of management of communicating |
764 | | * with the child process with the means of the correct |
765 | | * file descriptor. |
766 | | */ |
767 | | class Communication |
768 | | { |
769 | | public: |
770 | 40 | Communication(Streams* stream): stream_(stream) |
771 | 40 | {} |
772 | | Communication(const Communication&) = delete; |
773 | | Communication& operator=(const Communication&) = delete; |
774 | | Communication(Communication&&) = default; |
775 | | Communication& operator=(Communication&&) = default; |
776 | | public: |
777 | | int send(const char* msg, size_t length); |
778 | | int send(const std::vector<char>& msg); |
779 | | |
780 | | std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length); |
781 | | std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg) |
782 | 0 | { return communicate(msg.data(), msg.size()); } |
783 | | |
784 | 0 | void set_out_buf_cap(size_t cap) { out_buf_cap_ = cap; } |
785 | 0 | void set_err_buf_cap(size_t cap) { err_buf_cap_ = cap; } |
786 | | |
787 | | private: |
788 | | std::pair<OutBuffer, ErrBuffer> communicate_threaded( |
789 | | const char* msg, size_t length); |
790 | | |
791 | | private: |
792 | | Streams* stream_; |
793 | | size_t out_buf_cap_ = DEFAULT_BUF_CAP_BYTES; |
794 | | size_t err_buf_cap_ = DEFAULT_BUF_CAP_BYTES; |
795 | | }; |
796 | | |
797 | | |
798 | | |
799 | | /*! |
800 | | * This is a helper class to Popen. |
801 | | * It takes care of management of all the file descriptors |
802 | | * and file pointers. |
803 | | * It dispatches of the communication aspects to the |
804 | | * Communication class. |
805 | | * Read through the data members to understand about the |
806 | | * various file descriptors used. |
807 | | */ |
808 | | class Streams |
809 | | { |
810 | | public: |
811 | 40 | Streams():comm_(this) {} |
812 | | Streams(const Streams&) = delete; |
813 | | Streams& operator=(const Streams&) = delete; |
814 | | Streams(Streams&&) = default; |
815 | | Streams& operator=(Streams&&) = default; |
816 | | |
817 | | public: |
818 | | void setup_comm_channels(); |
819 | | |
820 | | void cleanup_fds() |
821 | 2 | { |
822 | 2 | if (write_to_child_ != -1 && read_from_parent_ != -1) { |
823 | 2 | subprocess_close(write_to_child_); |
824 | 2 | } |
825 | 2 | if (write_to_parent_ != -1 && read_from_child_ != -1) { |
826 | 2 | subprocess_close(read_from_child_); |
827 | 2 | } |
828 | 2 | if (err_write_ != -1 && err_read_ != -1) { |
829 | 2 | subprocess_close(err_read_); |
830 | 2 | } |
831 | 2 | } |
832 | | |
833 | | void close_parent_fds() |
834 | 0 | { |
835 | 0 | if (write_to_child_ != -1) subprocess_close(write_to_child_); |
836 | 0 | if (read_from_child_ != -1) subprocess_close(read_from_child_); |
837 | 0 | if (err_read_ != -1) subprocess_close(err_read_); |
838 | 0 | } |
839 | | |
840 | | void close_child_fds() |
841 | 40 | { |
842 | 40 | if (write_to_parent_ != -1) subprocess_close(write_to_parent_); |
843 | 40 | if (read_from_parent_ != -1) subprocess_close(read_from_parent_); |
844 | 40 | if (err_write_ != -1) subprocess_close(err_write_); |
845 | 40 | } |
846 | | |
847 | 124 | FILE* input() { return input_.get(); } |
848 | 154 | FILE* output() { return output_.get(); } |
849 | 154 | FILE* error() { return error_.get(); } |
850 | | |
851 | 40 | void input(FILE* fp) { input_.reset(fp, fclose); } |
852 | 40 | void output(FILE* fp) { output_.reset(fp, fclose); } |
853 | 40 | void error(FILE* fp) { error_.reset(fp, fclose); } |
854 | | |
855 | 0 | void set_out_buf_cap(size_t cap) { comm_.set_out_buf_cap(cap); } |
856 | 0 | void set_err_buf_cap(size_t cap) { comm_.set_err_buf_cap(cap); } |
857 | | |
858 | | public: /* Communication forwarding API's */ |
859 | | int send(const char* msg, size_t length) |
860 | 4 | { return comm_.send(msg, length); } |
861 | | |
862 | | int send(const std::vector<char>& msg) |
863 | 0 | { return comm_.send(msg); } |
864 | | |
865 | | std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length) |
866 | 38 | { return comm_.communicate(msg, length); } |
867 | | |
868 | | std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg) |
869 | 0 | { return comm_.communicate(msg); } |
870 | | |
871 | | |
872 | | public:// Yes they are public |
873 | | |
874 | | std::shared_ptr<FILE> input_ = nullptr; |
875 | | std::shared_ptr<FILE> output_ = nullptr; |
876 | | std::shared_ptr<FILE> error_ = nullptr; |
877 | | |
878 | | #ifdef WIN32 |
879 | | HANDLE g_hChildStd_IN_Rd = nullptr; |
880 | | HANDLE g_hChildStd_IN_Wr = nullptr; |
881 | | HANDLE g_hChildStd_OUT_Rd = nullptr; |
882 | | HANDLE g_hChildStd_OUT_Wr = nullptr; |
883 | | HANDLE g_hChildStd_ERR_Rd = nullptr; |
884 | | HANDLE g_hChildStd_ERR_Wr = nullptr; |
885 | | #endif |
886 | | |
887 | | // Pipes for communicating with child |
888 | | |
889 | | // Emulates stdin |
890 | | int write_to_child_ = -1; // Parent owned descriptor |
891 | | int read_from_parent_ = -1; // Child owned descriptor |
892 | | |
893 | | // Emulates stdout |
894 | | int write_to_parent_ = -1; // Child owned descriptor |
895 | | int read_from_child_ = -1; // Parent owned descriptor |
896 | | |
897 | | // Emulates stderr |
898 | | int err_write_ = -1; // Write error to parent (Child owned) |
899 | | int err_read_ = -1; // Read error from child (Parent owned) |
900 | | |
901 | | private: |
902 | | Communication comm_; |
903 | | }; |
904 | | |
905 | | } // end namespace detail |
906 | | |
907 | | |
908 | | |
909 | | /*! |
910 | | * class: Popen |
911 | | * This is the single most important class in the whole library |
912 | | * and glues together all the helper classes to provide a common |
913 | | * interface to the client. |
914 | | * |
915 | | * API's provided by the class: |
916 | | * Popen({"cmd"}, output{..}, error{..}, ....) |
917 | | * Command provided as a sequence. |
918 | | * wait() - Wait for the child to exit. |
919 | | * retcode() - The return code of the exited child. |
920 | | * send(...) - Send input to the input channel of the child. |
921 | | * communicate(...) - Get the output/error from the child and close the channels |
922 | | * from the parent side. |
923 | | */ |
924 | | class Popen |
925 | | { |
926 | | public: |
927 | | friend struct detail::ArgumentDeducer; |
928 | | #ifndef WIN32 |
929 | | friend class detail::Child; |
930 | | #endif |
931 | | |
932 | | template <typename... Args> |
933 | | Popen(std::initializer_list<const char*> cmd_args, Args&& ...args) |
934 | | { |
935 | | vargs_.insert(vargs_.end(), cmd_args.begin(), cmd_args.end()); |
936 | | init_args(std::forward<Args>(args)...); |
937 | | |
938 | | // Setup the communication channels of the Popen class |
939 | | stream_.setup_comm_channels(); |
940 | | |
941 | | execute_process(); |
942 | | } |
943 | | |
944 | | template <typename... Args> |
945 | 40 | Popen(std::vector<std::string> vargs_, Args &&... args) : vargs_(vargs_) |
946 | 40 | { |
947 | 40 | init_args(std::forward<Args>(args)...); |
948 | | |
949 | | // Setup the communication channels of the Popen class |
950 | 40 | stream_.setup_comm_channels(); |
951 | | |
952 | 40 | execute_process(); |
953 | 40 | } |
954 | | |
955 | 38 | int retcode() const noexcept { return retcode_; } |
956 | | |
957 | | int wait() noexcept(false); |
958 | | |
959 | 0 | void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); } |
960 | | |
961 | 0 | void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); } |
962 | | |
963 | | int send(const char* msg, size_t length) |
964 | 4 | { return stream_.send(msg, length); } |
965 | | |
966 | | int send(const std::string& msg) |
967 | 4 | { return send(msg.c_str(), msg.size()); } |
968 | | |
969 | | int send(const std::vector<char>& msg) |
970 | 0 | { return stream_.send(msg); } |
971 | | |
972 | | std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length) |
973 | 38 | { |
974 | 38 | auto res = stream_.communicate(msg, length); |
975 | 38 | retcode_ = wait(); |
976 | 38 | return res; |
977 | 38 | } |
978 | | |
979 | | std::pair<OutBuffer, ErrBuffer> communicate(const std::string& msg) |
980 | 0 | { |
981 | 0 | return communicate(msg.c_str(), msg.size()); |
982 | 0 | } |
983 | | |
984 | | std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg) |
985 | 0 | { |
986 | 0 | auto res = stream_.communicate(msg); |
987 | 0 | retcode_ = wait(); |
988 | 0 | return res; |
989 | 0 | } |
990 | | |
991 | | std::pair<OutBuffer, ErrBuffer> communicate() |
992 | 38 | { |
993 | 38 | return communicate(nullptr, 0); |
994 | 38 | } |
995 | | |
996 | | private: |
997 | | template <typename F, typename... Args> |
998 | | void init_args(F&& farg, Args&&... args); |
999 | | void init_args(); |
1000 | | void populate_c_argv(); |
1001 | | void execute_process() noexcept(false); |
1002 | | |
1003 | | private: |
1004 | | detail::Streams stream_; |
1005 | | |
1006 | | #ifdef WIN32 |
1007 | | HANDLE process_handle_; |
1008 | | std::future<void> cleanup_future_; |
1009 | | #else |
1010 | | // Pid of the child process |
1011 | | int child_pid_ = -1; |
1012 | | #endif |
1013 | | |
1014 | | std::string exe_name_; |
1015 | | |
1016 | | // Command provided as sequence |
1017 | | std::vector<std::string> vargs_; |
1018 | | std::vector<char*> cargv_; |
1019 | | |
1020 | | int retcode_ = -1; |
1021 | | }; |
1022 | | |
1023 | 40 | inline void Popen::init_args() { |
1024 | 40 | populate_c_argv(); |
1025 | 40 | } |
1026 | | |
1027 | | template <typename F, typename... Args> |
1028 | | inline void Popen::init_args(F&& farg, Args&&... args) |
1029 | 120 | { |
1030 | 120 | detail::ArgumentDeducer argd(this); |
1031 | 120 | argd.set_option(std::forward<F>(farg)); |
1032 | 120 | init_args(std::forward<Args>(args)...); |
1033 | 120 | } void subprocess::Popen::init_args<subprocess::input, subprocess::output, subprocess::error>(subprocess::input&&, subprocess::output&&, subprocess::error&&) Line | Count | Source | 1029 | 40 | { | 1030 | 40 | detail::ArgumentDeducer argd(this); | 1031 | 40 | argd.set_option(std::forward<F>(farg)); | 1032 | 40 | init_args(std::forward<Args>(args)...); | 1033 | 40 | } |
void subprocess::Popen::init_args<subprocess::output, subprocess::error>(subprocess::output&&, subprocess::error&&) Line | Count | Source | 1029 | 40 | { | 1030 | 40 | detail::ArgumentDeducer argd(this); | 1031 | 40 | argd.set_option(std::forward<F>(farg)); | 1032 | 40 | init_args(std::forward<Args>(args)...); | 1033 | 40 | } |
void subprocess::Popen::init_args<subprocess::error>(subprocess::error&&) Line | Count | Source | 1029 | 40 | { | 1030 | 40 | detail::ArgumentDeducer argd(this); | 1031 | 40 | argd.set_option(std::forward<F>(farg)); | 1032 | 40 | init_args(std::forward<Args>(args)...); | 1033 | 40 | } |
|
1034 | | |
1035 | | inline void Popen::populate_c_argv() |
1036 | 40 | { |
1037 | 40 | cargv_.clear(); |
1038 | 40 | cargv_.reserve(vargs_.size() + 1); |
1039 | 161 | for (auto& arg : vargs_) cargv_.push_back(&arg[0]); |
1040 | 40 | cargv_.push_back(nullptr); |
1041 | 40 | } |
1042 | | |
1043 | | inline int Popen::wait() noexcept(false) |
1044 | 40 | { |
1045 | | #ifdef WIN32 |
1046 | | int ret = WaitForSingleObject(process_handle_, INFINITE); |
1047 | | |
1048 | | // WaitForSingleObject with INFINITE should only return when process has signaled |
1049 | | if (ret != WAIT_OBJECT_0) { |
1050 | | throw OSError("Unexpected return code from WaitForSingleObject", 0); |
1051 | | } |
1052 | | |
1053 | | DWORD dretcode_; |
1054 | | |
1055 | | if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_)) |
1056 | | throw OSError("Failed during call to GetExitCodeProcess", 0); |
1057 | | |
1058 | | CloseHandle(process_handle_); |
1059 | | |
1060 | | return (int)dretcode_; |
1061 | | #else |
1062 | 40 | int ret, status; |
1063 | 40 | std::tie(ret, status) = util::wait_for_child_exit(child_pid_); |
1064 | 40 | if (ret == -1) { |
1065 | 0 | if (errno != ECHILD) throw OSError("waitpid failed", errno); |
1066 | 0 | return 0; |
1067 | 0 | } |
1068 | 40 | if (WIFEXITED(status)) return WEXITSTATUS(status); |
1069 | 0 | if (WIFSIGNALED(status)) return WTERMSIG(status); |
1070 | 0 | else return 255; |
1071 | | |
1072 | 0 | return 0; |
1073 | 0 | #endif |
1074 | 0 | } |
1075 | | |
1076 | | inline void Popen::execute_process() noexcept(false) |
1077 | 40 | { |
1078 | | #ifdef WIN32 |
1079 | | if (exe_name_.length()) { |
1080 | | this->vargs_.insert(this->vargs_.begin(), this->exe_name_); |
1081 | | this->populate_c_argv(); |
1082 | | } |
1083 | | this->exe_name_ = vargs_[0]; |
1084 | | |
1085 | | std::string argument; |
1086 | | std::string command_line; |
1087 | | bool first_arg = true; |
1088 | | |
1089 | | for (auto arg : this->vargs_) { |
1090 | | if (!first_arg) { |
1091 | | command_line += " "; |
1092 | | } else { |
1093 | | first_arg = false; |
1094 | | } |
1095 | | argument = arg; |
1096 | | util::quote_argument(argument, command_line, /*force=*/false); |
1097 | | } |
1098 | | |
1099 | | // CreateProcessA can modify szCmdLine so we allocate needed memory |
1100 | | char *szCmdline = new char[command_line.size() + 1]; |
1101 | | strcpy_s(szCmdline, command_line.size() + 1, command_line.c_str()); |
1102 | | PROCESS_INFORMATION piProcInfo; |
1103 | | STARTUPINFOA siStartInfo; |
1104 | | BOOL bSuccess = FALSE; |
1105 | | DWORD creation_flags = CREATE_NO_WINDOW; |
1106 | | |
1107 | | // Set up members of the PROCESS_INFORMATION structure. |
1108 | | ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); |
1109 | | |
1110 | | // Set up members of the STARTUPINFOA structure. |
1111 | | // This structure specifies the STDIN and STDOUT handles for redirection. |
1112 | | |
1113 | | ZeroMemory(&siStartInfo, sizeof(STARTUPINFOA)); |
1114 | | siStartInfo.cb = sizeof(STARTUPINFOA); |
1115 | | |
1116 | | siStartInfo.hStdError = this->stream_.g_hChildStd_ERR_Wr; |
1117 | | siStartInfo.hStdOutput = this->stream_.g_hChildStd_OUT_Wr; |
1118 | | siStartInfo.hStdInput = this->stream_.g_hChildStd_IN_Rd; |
1119 | | |
1120 | | siStartInfo.dwFlags |= STARTF_USESTDHANDLES; |
1121 | | |
1122 | | // Create the child process. |
1123 | | bSuccess = CreateProcessA(NULL, |
1124 | | szCmdline, // command line |
1125 | | NULL, // process security attributes |
1126 | | NULL, // primary thread security attributes |
1127 | | TRUE, // handles are inherited |
1128 | | creation_flags, // creation flags |
1129 | | NULL, // use parent's environment |
1130 | | NULL, // use parent's current directory |
1131 | | &siStartInfo, // STARTUPINFOA pointer |
1132 | | &piProcInfo); // receives PROCESS_INFORMATION |
1133 | | |
1134 | | // If an error occurs, exit the application. |
1135 | | if (!bSuccess) { |
1136 | | DWORD errorMessageID = ::GetLastError(); |
1137 | | throw CalledProcessError("CreateProcess failed: " + util::get_last_error(errorMessageID), errorMessageID); |
1138 | | } |
1139 | | |
1140 | | CloseHandle(piProcInfo.hThread); |
1141 | | |
1142 | | /* |
1143 | | TODO: use common apis to close linux handles |
1144 | | */ |
1145 | | |
1146 | | this->process_handle_ = piProcInfo.hProcess; |
1147 | | |
1148 | | this->cleanup_future_ = std::async(std::launch::async, [this] { |
1149 | | WaitForSingleObject(this->process_handle_, INFINITE); |
1150 | | |
1151 | | CloseHandle(this->stream_.g_hChildStd_ERR_Wr); |
1152 | | CloseHandle(this->stream_.g_hChildStd_OUT_Wr); |
1153 | | CloseHandle(this->stream_.g_hChildStd_IN_Rd); |
1154 | | }); |
1155 | | |
1156 | | /* |
1157 | | NOTE: In the linux version, there is a check to make sure that the process |
1158 | | has been started. Here, we do nothing because CreateProcess will throw |
1159 | | if we fail to create the process. |
1160 | | */ |
1161 | | |
1162 | | |
1163 | | #else |
1164 | | |
1165 | 40 | int err_rd_pipe, err_wr_pipe; |
1166 | 40 | std::tie(err_rd_pipe, err_wr_pipe) = util::pipe_cloexec(); |
1167 | | |
1168 | 40 | if (exe_name_.length()) { |
1169 | 0 | vargs_.insert(vargs_.begin(), exe_name_); |
1170 | 0 | populate_c_argv(); |
1171 | 0 | } |
1172 | 40 | exe_name_ = vargs_[0]; |
1173 | | |
1174 | 40 | child_pid_ = fork(); |
1175 | | |
1176 | 40 | if (child_pid_ < 0) { |
1177 | 0 | subprocess_close(err_rd_pipe); |
1178 | 0 | subprocess_close(err_wr_pipe); |
1179 | 0 | throw OSError("fork failed", errno); |
1180 | 0 | } |
1181 | | |
1182 | 40 | if (child_pid_ == 0) |
1183 | 0 | { |
1184 | | // Close descriptors belonging to parent |
1185 | 0 | stream_.close_parent_fds(); |
1186 | | |
1187 | | //Close the read end of the error pipe |
1188 | 0 | subprocess_close(err_rd_pipe); |
1189 | |
|
1190 | 0 | detail::Child chld(this, err_wr_pipe); |
1191 | 0 | chld.execute_child(); |
1192 | 0 | } |
1193 | 40 | else |
1194 | 40 | { |
1195 | 40 | subprocess_close(err_wr_pipe);// close child side of pipe, else get stuck in read below |
1196 | | |
1197 | 40 | stream_.close_child_fds(); |
1198 | | |
1199 | 40 | try { |
1200 | 40 | char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,}; |
1201 | | |
1202 | 40 | FILE* err_fp = fdopen(err_rd_pipe, "r"); |
1203 | 40 | if (!err_fp) { |
1204 | 0 | subprocess_close(err_rd_pipe); |
1205 | 0 | throw OSError("fdopen failed", errno); |
1206 | 0 | } |
1207 | 40 | int read_bytes = util::read_atmost_n(err_fp, err_buf, SP_MAX_ERR_BUF_SIZ); |
1208 | 40 | fclose(err_fp); |
1209 | | |
1210 | 40 | if (read_bytes || strlen(err_buf)) { |
1211 | | // Call waitpid to reap the child process |
1212 | | // waitpid suspends the calling process until the |
1213 | | // child terminates. |
1214 | 2 | int retcode = wait(); |
1215 | | |
1216 | | // Throw whatever information we have about child failure |
1217 | 2 | throw CalledProcessError(err_buf, retcode); |
1218 | 2 | } |
1219 | 40 | } catch (std::exception& exp) { |
1220 | 2 | stream_.cleanup_fds(); |
1221 | 2 | throw; |
1222 | 2 | } |
1223 | | |
1224 | 40 | } |
1225 | 40 | #endif |
1226 | 40 | } |
1227 | | |
1228 | | namespace detail { |
1229 | | |
1230 | 0 | inline void ArgumentDeducer::set_option(executable&& exe) { |
1231 | 0 | popen_->exe_name_ = std::move(exe.arg_value); |
1232 | 0 | } |
1233 | | |
1234 | 40 | inline void ArgumentDeducer::set_option(input&& inp) { |
1235 | 40 | if (inp.rd_ch_ != -1) popen_->stream_.read_from_parent_ = inp.rd_ch_; |
1236 | 40 | if (inp.wr_ch_ != -1) popen_->stream_.write_to_child_ = inp.wr_ch_; |
1237 | 40 | } |
1238 | | |
1239 | 40 | inline void ArgumentDeducer::set_option(output&& out) { |
1240 | 40 | if (out.wr_ch_ != -1) popen_->stream_.write_to_parent_ = out.wr_ch_; |
1241 | 40 | if (out.rd_ch_ != -1) popen_->stream_.read_from_child_ = out.rd_ch_; |
1242 | 40 | } |
1243 | | |
1244 | 40 | inline void ArgumentDeducer::set_option(error&& err) { |
1245 | 40 | if (err.deferred_) { |
1246 | 0 | if (popen_->stream_.write_to_parent_) { |
1247 | 0 | popen_->stream_.err_write_ = popen_->stream_.write_to_parent_; |
1248 | 0 | } else { |
1249 | 0 | throw std::runtime_error("Set output before redirecting error to output"); |
1250 | 0 | } |
1251 | 0 | } |
1252 | 40 | if (err.wr_ch_ != -1) popen_->stream_.err_write_ = err.wr_ch_; |
1253 | 40 | if (err.rd_ch_ != -1) popen_->stream_.err_read_ = err.rd_ch_; |
1254 | 40 | } |
1255 | | |
1256 | | |
1257 | | #ifndef WIN32 |
1258 | 0 | inline void Child::execute_child() { |
1259 | 0 | int sys_ret = -1; |
1260 | 0 | auto& stream = parent_->stream_; |
1261 | |
|
1262 | 0 | try { |
1263 | 0 | if (stream.write_to_parent_ == 0) |
1264 | 0 | stream.write_to_parent_ = dup(stream.write_to_parent_); |
1265 | |
|
1266 | 0 | if (stream.err_write_ == 0 || stream.err_write_ == 1) |
1267 | 0 | stream.err_write_ = dup(stream.err_write_); |
1268 | | |
1269 | | // Make the child owned descriptors as the |
1270 | | // stdin, stdout and stderr for the child process |
1271 | 0 | auto _dup2_ = [](int fd, int to_fd) { |
1272 | 0 | if (fd == to_fd) { |
1273 | | // dup2 syscall does not reset the |
1274 | | // CLOEXEC flag if the descriptors |
1275 | | // provided to it are same. |
1276 | | // But, we need to reset the CLOEXEC |
1277 | | // flag as the provided descriptors |
1278 | | // are now going to be the standard |
1279 | | // input, output and error |
1280 | 0 | util::set_clo_on_exec(fd, false); |
1281 | 0 | } else if(fd != -1) { |
1282 | 0 | int res = dup2(fd, to_fd); |
1283 | 0 | if (res == -1) throw OSError("dup2 failed", errno); |
1284 | 0 | } |
1285 | 0 | }; |
1286 | | |
1287 | | // Create the standard streams |
1288 | 0 | _dup2_(stream.read_from_parent_, 0); // Input stream |
1289 | 0 | _dup2_(stream.write_to_parent_, 1); // Output stream |
1290 | 0 | _dup2_(stream.err_write_, 2); // Error stream |
1291 | | |
1292 | | // Close the duped descriptors |
1293 | 0 | if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2) |
1294 | 0 | subprocess_close(stream.read_from_parent_); |
1295 | |
|
1296 | 0 | if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2) |
1297 | 0 | subprocess_close(stream.write_to_parent_); |
1298 | |
|
1299 | 0 | if (stream.err_write_ != -1 && stream.err_write_ > 2) |
1300 | 0 | subprocess_close(stream.err_write_); |
1301 | | |
1302 | | // Replace the current image with the executable |
1303 | 0 | sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data()); |
1304 | |
|
1305 | 0 | if (sys_ret == -1) throw OSError("execve failed", errno); |
1306 | |
|
1307 | 0 | } catch (const OSError& exp) { |
1308 | | // Just write the exception message |
1309 | | // TODO: Give back stack trace ? |
1310 | 0 | std::string err_msg(exp.what()); |
1311 | | //ATTN: Can we do something on error here ? |
1312 | 0 | util::write_n(err_wr_pipe_, err_msg.c_str(), err_msg.length()); |
1313 | 0 | } |
1314 | | |
1315 | | // Calling application would not get this |
1316 | | // exit failure |
1317 | 0 | _exit (EXIT_FAILURE); |
1318 | 0 | } |
1319 | | #endif |
1320 | | |
1321 | | |
1322 | | inline void Streams::setup_comm_channels() |
1323 | 40 | { |
1324 | | #ifdef WIN32 |
1325 | | util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr); |
1326 | | this->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w")); |
1327 | | this->write_to_child_ = subprocess_fileno(this->input()); |
1328 | | |
1329 | | util::configure_pipe(&this->g_hChildStd_OUT_Rd, &this->g_hChildStd_OUT_Wr, &this->g_hChildStd_OUT_Rd); |
1330 | | this->output(util::file_from_handle(this->g_hChildStd_OUT_Rd, "r")); |
1331 | | this->read_from_child_ = subprocess_fileno(this->output()); |
1332 | | |
1333 | | util::configure_pipe(&this->g_hChildStd_ERR_Rd, &this->g_hChildStd_ERR_Wr, &this->g_hChildStd_ERR_Rd); |
1334 | | this->error(util::file_from_handle(this->g_hChildStd_ERR_Rd, "r")); |
1335 | | this->err_read_ = subprocess_fileno(this->error()); |
1336 | | #else |
1337 | | |
1338 | 40 | if (write_to_child_ != -1) input(fdopen(write_to_child_, "wb")); |
1339 | 40 | if (read_from_child_ != -1) output(fdopen(read_from_child_, "rb")); |
1340 | 40 | if (err_read_ != -1) error(fdopen(err_read_, "rb")); |
1341 | | |
1342 | 40 | auto handles = {input(), output(), error()}; |
1343 | | |
1344 | 120 | for (auto& h : handles) { |
1345 | 120 | if (h == nullptr) continue; |
1346 | 120 | setvbuf(h, nullptr, _IONBF, BUFSIZ); |
1347 | 120 | } |
1348 | 40 | #endif |
1349 | 40 | } |
1350 | | |
1351 | | inline int Communication::send(const char* msg, size_t length) |
1352 | 4 | { |
1353 | 4 | if (stream_->input() == nullptr) return -1; |
1354 | 4 | return std::fwrite(msg, sizeof(char), length, stream_->input()); |
1355 | 4 | } |
1356 | | |
1357 | | inline int Communication::send(const std::vector<char>& msg) |
1358 | 0 | { |
1359 | 0 | return send(msg.data(), msg.size()); |
1360 | 0 | } |
1361 | | |
1362 | | inline std::pair<OutBuffer, ErrBuffer> |
1363 | | Communication::communicate(const char* msg, size_t length) |
1364 | 38 | { |
1365 | | // Optimization from subprocess.py |
1366 | | // If we are using one pipe, or no pipe |
1367 | | // at all, using select() or threads is unnecessary. |
1368 | 38 | auto hndls = {stream_->input(), stream_->output(), stream_->error()}; |
1369 | 38 | int count = std::count(std::begin(hndls), std::end(hndls), nullptr); |
1370 | 38 | const int len_conv = length; |
1371 | | |
1372 | 38 | if (count >= 2) { |
1373 | 0 | OutBuffer obuf; |
1374 | 0 | ErrBuffer ebuf; |
1375 | 0 | if (stream_->input()) { |
1376 | 0 | if (msg) { |
1377 | 0 | int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input()); |
1378 | 0 | if (wbytes < len_conv) { |
1379 | 0 | if (errno != EPIPE && errno != EINVAL) { |
1380 | 0 | throw OSError("fwrite error", errno); |
1381 | 0 | } |
1382 | 0 | } |
1383 | 0 | } |
1384 | | // Close the input stream |
1385 | 0 | stream_->input_.reset(); |
1386 | 0 | } else if (stream_->output()) { |
1387 | | // Read till EOF |
1388 | | // ATTN: This could be blocking, if the process |
1389 | | // at the other end screws up, we get screwed as well |
1390 | 0 | obuf.add_cap(out_buf_cap_); |
1391 | |
|
1392 | 0 | int rbytes = util::read_all( |
1393 | 0 | stream_->output(), |
1394 | 0 | obuf.buf); |
1395 | |
|
1396 | 0 | if (rbytes == -1) { |
1397 | 0 | throw OSError("read to obuf failed", errno); |
1398 | 0 | } |
1399 | | |
1400 | 0 | obuf.length = rbytes; |
1401 | | // Close the output stream |
1402 | 0 | stream_->output_.reset(); |
1403 | |
|
1404 | 0 | } else if (stream_->error()) { |
1405 | | // Same screwness applies here as well |
1406 | 0 | ebuf.add_cap(err_buf_cap_); |
1407 | |
|
1408 | 0 | int rbytes = util::read_atmost_n( |
1409 | 0 | stream_->error(), |
1410 | 0 | ebuf.buf.data(), |
1411 | 0 | ebuf.buf.size()); |
1412 | |
|
1413 | 0 | if (rbytes == -1) { |
1414 | 0 | throw OSError("read to ebuf failed", errno); |
1415 | 0 | } |
1416 | | |
1417 | 0 | ebuf.length = rbytes; |
1418 | | // Close the error stream |
1419 | 0 | stream_->error_.reset(); |
1420 | 0 | } |
1421 | 0 | return std::make_pair(std::move(obuf), std::move(ebuf)); |
1422 | 0 | } |
1423 | | |
1424 | 38 | return communicate_threaded(msg, length); |
1425 | 38 | } |
1426 | | |
1427 | | |
1428 | | inline std::pair<OutBuffer, ErrBuffer> |
1429 | | Communication::communicate_threaded(const char* msg, size_t length) |
1430 | 38 | { |
1431 | 38 | OutBuffer obuf; |
1432 | 38 | ErrBuffer ebuf; |
1433 | 38 | std::future<int> out_fut, err_fut; |
1434 | 38 | const int length_conv = length; |
1435 | | |
1436 | 38 | if (stream_->output()) { |
1437 | 38 | obuf.add_cap(out_buf_cap_); |
1438 | | |
1439 | 38 | out_fut = std::async(std::launch::async, |
1440 | 38 | [&obuf, this] { |
1441 | 38 | return util::read_all(this->stream_->output(), obuf.buf); |
1442 | 38 | }); |
1443 | 38 | } |
1444 | 38 | if (stream_->error()) { |
1445 | 38 | ebuf.add_cap(err_buf_cap_); |
1446 | | |
1447 | 38 | err_fut = std::async(std::launch::async, |
1448 | 38 | [&ebuf, this] { |
1449 | 38 | return util::read_all(this->stream_->error(), ebuf.buf); |
1450 | 38 | }); |
1451 | 38 | } |
1452 | 38 | if (stream_->input()) { |
1453 | 38 | if (msg) { |
1454 | 0 | int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input()); |
1455 | 0 | if (wbytes < length_conv) { |
1456 | 0 | if (errno != EPIPE && errno != EINVAL) { |
1457 | 0 | throw OSError("fwrite error", errno); |
1458 | 0 | } |
1459 | 0 | } |
1460 | 0 | } |
1461 | 38 | stream_->input_.reset(); |
1462 | 38 | } |
1463 | | |
1464 | 38 | if (out_fut.valid()) { |
1465 | 38 | int res = out_fut.get(); |
1466 | 38 | if (res != -1) obuf.length = res; |
1467 | 0 | else obuf.length = 0; |
1468 | 38 | } |
1469 | 38 | if (err_fut.valid()) { |
1470 | 38 | int res = err_fut.get(); |
1471 | 38 | if (res != -1) ebuf.length = res; |
1472 | 0 | else ebuf.length = 0; |
1473 | 38 | } |
1474 | | |
1475 | 38 | return std::make_pair(std::move(obuf), std::move(ebuf)); |
1476 | 38 | } |
1477 | | |
1478 | | } // end namespace detail |
1479 | | |
1480 | | } |
1481 | | |
1482 | | #endif // BITCOIN_UTIL_SUBPROCESS_H |