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