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