subprocess.h raw
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 LIMENKA_UTIL_SUBPROCESS_H
37 #define LIMENKA_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 #define subprocess_close close
93 #define subprocess_fileno fileno
94 #define subprocess_open open
95 #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 std::runtime_error(error_msg), retcode(retcode)
151 {}
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 std::runtime_error(err_msg + ": " + SysErrorString(err_code))
170 {}
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 {
319 std::vector<std::string> res;
320 size_t init = 0;
321
322 while (true) {
323 auto pos = str.find_first_of(delims, init);
324 if (pos == std::string::npos) {
325 res.emplace_back(str.substr(init, str.length()));
326 break;
327 }
328 res.emplace_back(str.substr(init, pos - init));
329 pos++;
330 init = pos;
331 }
332
333 return res;
334 }
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 {
350 int flags = fcntl(fd, F_GETFD, 0);
351 if (flags == -1) {
352 throw OSError("fcntl F_GETFD failed", errno);
353 }
354 if (set) flags |= FD_CLOEXEC;
355 else flags &= ~FD_CLOEXEC;
356 if (fcntl(fd, F_SETFD, flags) == -1) {
357 throw OSError("fcntl F_SETFD failed", errno);
358 }
359 }
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 {
374 int pipe_fds[2];
375 int res = pipe(pipe_fds);
376 if (res) {
377 throw OSError("pipe failure", errno);
378 }
379
380 set_clo_on_exec(pipe_fds[0]);
381 set_clo_on_exec(pipe_fds[1]);
382
383 return std::make_pair(pipe_fds[0], pipe_fds[1]);
384 }
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 {
402 size_t nwritten = 0;
403 while (nwritten < length) {
404 int written = subprocess_write(fd, buf + nwritten, length - nwritten);
405 if (written == -1) return -1;
406 nwritten += written;
407 }
408 return nwritten;
409 }
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 {
429 #ifdef __USING_WINDOWS__
430 return (int)fread(buf, 1, read_upto, fp);
431 #else
432 int fd = subprocess_fileno(fp);
433 int rbytes = 0;
434 int eintr_cnter = 0;
435
436 while (1) {
437 int read_bytes = read(fd, buf + rbytes, read_upto - rbytes);
438 if (read_bytes == -1) {
439 if (errno == EINTR) {
440 if (eintr_cnter >= 50) return -1;
441 eintr_cnter++;
442 continue;
443 }
444 return -1;
445 }
446 if (read_bytes == 0) return rbytes;
447
448 rbytes += read_bytes;
449 }
450 return rbytes;
451 #endif
452 }
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 {
470 auto buffer = buf.data();
471 int total_bytes_read = 0;
472 int fill_sz = buf.size();
473
474 while (1) {
475 const int rd_bytes = read_atmost_n(fp, buffer, fill_sz);
476
477 if (rd_bytes == -1) { // Read finished
478 if (total_bytes_read == 0) return -1;
479 break;
480
481 } else if (rd_bytes == fill_sz) { // Buffer full
482 const auto orig_sz = buf.size();
483 const auto new_sz = orig_sz * 2;
484 buf.resize(new_sz);
485 fill_sz = new_sz - orig_sz;
486
487 //update the buffer pointer
488 buffer = buf.data();
489 total_bytes_read += rd_bytes;
490 buffer += total_bytes_read;
491
492 } else { // Partial data ? Continue reading
493 total_bytes_read += rd_bytes;
494 fill_sz -= rd_bytes;
495 break;
496 }
497 }
498 buf.erase(buf.begin()+total_bytes_read, buf.end()); // remove extra nulls
499 return total_bytes_read;
500 }
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 {
519 int status = 0;
520 int ret = -1;
521 while (1) {
522 ret = waitpid(pid, &status, 0);
523 if (ret == -1) break;
524 if (ret == 0) continue;
525 return std::make_pair(ret, status);
526 }
527
528 return std::make_pair(ret, status);
529 }
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 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 string_arg(const char* arg): arg_value(arg) {}
561 string_arg(std::string&& arg): arg_value(std::move(arg)) {}
562 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 explicit input(int fd): rd_ch_(fd) {}
606
607 // FILE pointer.
608 explicit input (FILE* fp):input(subprocess_fileno(fp)) { assert(fp); }
609
610 explicit input(const char* filename) {
611 int fd = subprocess_open(filename, O_RDONLY);
612 if (fd == -1) throw OSError("File not found: ", errno);
613 rd_ch_ = fd;
614 }
615 explicit input(IOTYPE typ) {
616 assert (typ == PIPE && "STDOUT/STDERR not allowed");
617 #ifndef __USING_WINDOWS__
618 std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
619 #endif
620 }
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 explicit output(int fd): wr_ch_(fd) {}
640
641 explicit output (FILE* fp):output(subprocess_fileno(fp)) { assert(fp); }
642
643 explicit output(const char* filename) {
644 int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
645 if (fd == -1) throw OSError("File not found: ", errno);
646 wr_ch_ = fd;
647 }
648 explicit output(IOTYPE typ) {
649 assert (typ == PIPE && "STDOUT/STDERR not allowed");
650 #ifndef __USING_WINDOWS__
651 std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
652 #endif
653 }
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 explicit error(int fd): wr_ch_(fd) {}
671
672 explicit error(FILE* fp):error(subprocess_fileno(fp)) { assert(fp); }
673
674 explicit error(const char* filename) {
675 int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
676 if (fd == -1) throw OSError("File not found: ", errno);
677 wr_ch_ = fd;
678 }
679 explicit error(IOTYPE typ) {
680 assert ((typ == PIPE || typ == STDOUT) && "STDERR not allowed");
681 if (typ == PIPE) {
682 #ifndef __USING_WINDOWS__
683 std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
684 #endif
685 } else {
686 // Need to defer it till we have checked all arguments
687 deferred_ = true;
688 }
689 }
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 Buffer() = default;
714 explicit Buffer(size_t cap) { buf.resize(cap); }
715 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 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 parent_(p),
768 err_wr_pipe_(err_wr_pipe)
769 {}
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 Communication(Streams* stream): stream_(stream)
793 {}
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 { return communicate(msg.data(), msg.size()); }
805
806 void set_out_buf_cap(size_t cap) { out_buf_cap_ = cap; }
807 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 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 {
844 if (write_to_child_ != -1 && read_from_parent_ != -1) {
845 subprocess_close(write_to_child_);
846 }
847 if (write_to_parent_ != -1 && read_from_child_ != -1) {
848 subprocess_close(read_from_child_);
849 }
850 if (err_write_ != -1 && err_read_ != -1) {
851 subprocess_close(err_read_);
852 }
853 }
854
855 void close_parent_fds()
856 {
857 if (write_to_child_ != -1) subprocess_close(write_to_child_);
858 if (read_from_child_ != -1) subprocess_close(read_from_child_);
859 if (err_read_ != -1) subprocess_close(err_read_);
860 }
861
862 void close_child_fds()
863 {
864 if (write_to_parent_ != -1) subprocess_close(write_to_parent_);
865 if (read_from_parent_ != -1) subprocess_close(read_from_parent_);
866 if (err_write_ != -1) subprocess_close(err_write_);
867 }
868
869 FILE* input() { return input_.get(); }
870 FILE* output() { return output_.get(); }
871 FILE* error() { return error_.get(); }
872
873 void input(FILE* fp) { input_.reset(fp, fclose); }
874 void output(FILE* fp) { output_.reset(fp, fclose); }
875 void error(FILE* fp) { error_.reset(fp, fclose); }
876
877 void set_out_buf_cap(size_t cap) { comm_.set_out_buf_cap(cap); }
878 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 { return comm_.send(msg, length); }
883
884 int send(const std::vector<char>& msg)
885 { return comm_.send(msg); }
886
887 std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
888 { return comm_.communicate(msg, length); }
889
890 std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
891 { 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 * poll() - Check the status of the running child.
945 * kill(sig_num) - Kill the child. SIGTERM used by default.
946 * send(...) - Send input to the input channel of the child.
947 * communicate(...) - Get the output/error from the child and close the channels
948 * from the parent side.
949 */
950 class Popen
951 {
952 public:
953 friend struct detail::ArgumentDeducer;
954 friend class detail::Child;
955
956 template <typename... Args>
957 Popen(const std::string& cmd_args, Args&& ...args):
958 args_(cmd_args)
959 {
960 vargs_ = util::split(cmd_args);
961 init_args(std::forward<Args>(args)...);
962
963 // Setup the communication channels of the Popen class
964 stream_.setup_comm_channels();
965
966 execute_process();
967 }
968
969 template <typename... Args>
970 Popen(std::initializer_list<const char*> cmd_args, Args&& ...args)
971 {
972 vargs_.insert(vargs_.end(), cmd_args.begin(), cmd_args.end());
973 init_args(std::forward<Args>(args)...);
974
975 // Setup the communication channels of the Popen class
976 stream_.setup_comm_channels();
977
978 execute_process();
979 }
980
981 template <typename... Args>
982 Popen(std::vector<std::string> vargs_, Args &&... args) : vargs_(vargs_)
983 {
984 init_args(std::forward<Args>(args)...);
985
986 // Setup the communication channels of the Popen class
987 stream_.setup_comm_channels();
988
989 execute_process();
990 }
991
992 int retcode() const noexcept { return retcode_; }
993
994 int wait() noexcept(false);
995
996 int poll() noexcept(false);
997
998 // Does not fail, Caller is expected to recheck the
999 // status with a call to poll()
1000 void kill(int sig_num = 9);
1001
1002 void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
1003
1004 void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
1005
1006 int send(const char* msg, size_t length)
1007 { return stream_.send(msg, length); }
1008
1009 int send(const std::string& msg)
1010 { return send(msg.c_str(), msg.size()); }
1011
1012 int send(const std::vector<char>& msg)
1013 { return stream_.send(msg); }
1014
1015 std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
1016 {
1017 auto res = stream_.communicate(msg, length);
1018 retcode_ = wait();
1019 return res;
1020 }
1021
1022 std::pair<OutBuffer, ErrBuffer> communicate(const std::string& msg)
1023 {
1024 return communicate(msg.c_str(), msg.size());
1025 }
1026
1027 std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
1028 {
1029 auto res = stream_.communicate(msg);
1030 retcode_ = wait();
1031 return res;
1032 }
1033
1034 std::pair<OutBuffer, ErrBuffer> communicate()
1035 {
1036 return communicate(nullptr, 0);
1037 }
1038
1039 private:
1040 template <typename F, typename... Args>
1041 void init_args(F&& farg, Args&&... args);
1042 void init_args();
1043 void populate_c_argv();
1044 void execute_process() noexcept(false);
1045
1046 private:
1047 detail::Streams stream_;
1048
1049 #ifdef __USING_WINDOWS__
1050 HANDLE process_handle_;
1051 std::future<void> cleanup_future_;
1052 #endif
1053
1054 bool close_fds_ = false;
1055
1056 std::string exe_name_;
1057
1058 // Command in string format
1059 std::string args_;
1060 // Command provided as sequence
1061 std::vector<std::string> vargs_;
1062 std::vector<char*> cargv_;
1063
1064 bool child_created_ = false;
1065 // Pid of the child process
1066 int child_pid_ = -1;
1067
1068 int retcode_ = -1;
1069 };
1070
1071 inline void Popen::init_args() {
1072 populate_c_argv();
1073 }
1074
1075 template <typename F, typename... Args>
1076 inline void Popen::init_args(F&& farg, Args&&... args)
1077 {
1078 detail::ArgumentDeducer argd(this);
1079 argd.set_option(std::forward<F>(farg));
1080 init_args(std::forward<Args>(args)...);
1081 }
1082
1083 inline void Popen::populate_c_argv()
1084 {
1085 cargv_.clear();
1086 cargv_.reserve(vargs_.size() + 1);
1087 for (auto& arg : vargs_) cargv_.push_back(&arg[0]);
1088 cargv_.push_back(nullptr);
1089 }
1090
1091 inline int Popen::wait() noexcept(false)
1092 {
1093 #ifdef __USING_WINDOWS__
1094 int ret = WaitForSingleObject(process_handle_, INFINITE);
1095
1096 // WaitForSingleObject with INFINITE should only return when process has signaled
1097 if (ret != WAIT_OBJECT_0) {
1098 throw OSError("Unexpected return code from WaitForSingleObject", 0);
1099 }
1100
1101 DWORD dretcode_;
1102
1103 if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
1104 throw OSError("Failed during call to GetExitCodeProcess", 0);
1105
1106 CloseHandle(process_handle_);
1107
1108 return (int)dretcode_;
1109 #else
1110 int ret, status;
1111 std::tie(ret, status) = util::wait_for_child_exit(child_pid_);
1112 if (ret == -1) {
1113 if (errno != ECHILD) throw OSError("waitpid failed", errno);
1114 return 0;
1115 }
1116 if (WIFEXITED(status)) return WEXITSTATUS(status);
1117 if (WIFSIGNALED(status)) return WTERMSIG(status);
1118 else return 255;
1119
1120 return 0;
1121 #endif
1122 }
1123
1124 inline int Popen::poll() noexcept(false)
1125 {
1126 #ifdef __USING_WINDOWS__
1127 int ret = WaitForSingleObject(process_handle_, 0);
1128 if (ret != WAIT_OBJECT_0) return -1;
1129
1130 DWORD dretcode_;
1131 if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
1132 throw OSError("GetExitCodeProcess", 0);
1133
1134 retcode_ = (int)dretcode_;
1135 CloseHandle(process_handle_);
1136
1137 return retcode_;
1138 #else
1139 if (!child_created_) return -1; // TODO: ??
1140
1141 int status;
1142
1143 // Returns zero if child is still running
1144 int ret = waitpid(child_pid_, &status, WNOHANG);
1145 if (ret == 0) return -1;
1146
1147 if (ret == child_pid_) {
1148 if (WIFSIGNALED(status)) {
1149 retcode_ = WTERMSIG(status);
1150 } else if (WIFEXITED(status)) {
1151 retcode_ = WEXITSTATUS(status);
1152 } else {
1153 retcode_ = 255;
1154 }
1155 return retcode_;
1156 }
1157
1158 if (ret == -1) {
1159 // From subprocess.py
1160 // This happens if SIGCHLD is set to be ignored
1161 // or waiting for child process has otherwise been disabled
1162 // for our process. This child is dead, we cannot get the
1163 // status.
1164 if (errno == ECHILD) retcode_ = 0;
1165 else throw OSError("waitpid failed", errno);
1166 } else {
1167 retcode_ = ret;
1168 }
1169
1170 return retcode_;
1171 #endif
1172 }
1173
1174 inline void Popen::kill(int sig_num)
1175 {
1176 #ifdef __USING_WINDOWS__
1177 if (!TerminateProcess(this->process_handle_, (UINT)sig_num)) {
1178 throw OSError("TerminateProcess", 0);
1179 }
1180 #else
1181 ::kill(child_pid_, sig_num);
1182 #endif
1183 }
1184
1185
1186 inline void Popen::execute_process() noexcept(false)
1187 {
1188 #ifdef __USING_WINDOWS__
1189 if (exe_name_.length()) {
1190 this->vargs_.insert(this->vargs_.begin(), this->exe_name_);
1191 this->populate_c_argv();
1192 }
1193 this->exe_name_ = vargs_[0];
1194
1195 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
1196 std::wstring argument;
1197 std::wstring command_line;
1198 bool first_arg = true;
1199
1200 for (auto arg : this->vargs_) {
1201 if (!first_arg) {
1202 command_line += L" ";
1203 } else {
1204 first_arg = false;
1205 }
1206 argument = converter.from_bytes(arg);
1207 util::quote_argument(argument, command_line, false);
1208 }
1209
1210 // CreateProcessW can modify szCmdLine so we allocate needed memory
1211 wchar_t *szCmdline = new wchar_t[command_line.size() + 1];
1212 wcscpy_s(szCmdline, command_line.size() + 1, command_line.c_str());
1213 PROCESS_INFORMATION piProcInfo;
1214 STARTUPINFOW siStartInfo;
1215 BOOL bSuccess = FALSE;
1216 DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW;
1217
1218 // Set up members of the PROCESS_INFORMATION structure.
1219 ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
1220
1221 // Set up members of the STARTUPINFOW structure.
1222 // This structure specifies the STDIN and STDOUT handles for redirection.
1223
1224 ZeroMemory(&siStartInfo, sizeof(STARTUPINFOW));
1225 siStartInfo.cb = sizeof(STARTUPINFOW);
1226
1227 siStartInfo.hStdError = this->stream_.g_hChildStd_ERR_Wr;
1228 siStartInfo.hStdOutput = this->stream_.g_hChildStd_OUT_Wr;
1229 siStartInfo.hStdInput = this->stream_.g_hChildStd_IN_Rd;
1230
1231 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
1232
1233 // Create the child process.
1234 bSuccess = CreateProcessW(NULL,
1235 szCmdline, // command line
1236 NULL, // process security attributes
1237 NULL, // primary thread security attributes
1238 TRUE, // handles are inherited
1239 creation_flags, // creation flags
1240 NULL, // use parent's environment
1241 NULL, // use parent's current directory
1242 &siStartInfo, // STARTUPINFOW pointer
1243 &piProcInfo); // receives PROCESS_INFORMATION
1244
1245 // If an error occurs, exit the application.
1246 if (!bSuccess) {
1247 DWORD errorMessageID = ::GetLastError();
1248 throw CalledProcessError("CreateProcess failed: " + util::get_last_error(errorMessageID), errorMessageID);
1249 }
1250
1251 CloseHandle(piProcInfo.hThread);
1252
1253 /*
1254 TODO: use common apis to close linux handles
1255 */
1256
1257 this->process_handle_ = piProcInfo.hProcess;
1258
1259 this->cleanup_future_ = std::async(std::launch::async, [this] {
1260 WaitForSingleObject(this->process_handle_, INFINITE);
1261
1262 CloseHandle(this->stream_.g_hChildStd_ERR_Wr);
1263 CloseHandle(this->stream_.g_hChildStd_OUT_Wr);
1264 CloseHandle(this->stream_.g_hChildStd_IN_Rd);
1265 });
1266
1267 /*
1268 NOTE: In the linux version, there is a check to make sure that the process
1269 has been started. Here, we do nothing because CreateProcess will throw
1270 if we fail to create the process.
1271 */
1272
1273
1274 #else
1275
1276 int err_rd_pipe, err_wr_pipe;
1277 std::tie(err_rd_pipe, err_wr_pipe) = util::pipe_cloexec();
1278
1279 if (exe_name_.length()) {
1280 vargs_.insert(vargs_.begin(), exe_name_);
1281 populate_c_argv();
1282 }
1283 exe_name_ = vargs_[0];
1284
1285 child_pid_ = fork();
1286
1287 if (child_pid_ < 0) {
1288 subprocess_close(err_rd_pipe);
1289 subprocess_close(err_wr_pipe);
1290 throw OSError("fork failed", errno);
1291 }
1292
1293 child_created_ = true;
1294
1295 if (child_pid_ == 0)
1296 {
1297 // Close descriptors belonging to parent
1298 stream_.close_parent_fds();
1299
1300 //Close the read end of the error pipe
1301 subprocess_close(err_rd_pipe);
1302
1303 detail::Child chld(this, err_wr_pipe);
1304 chld.execute_child();
1305 }
1306 else
1307 {
1308 subprocess_close(err_wr_pipe);// close child side of pipe, else get stuck in read below
1309
1310 stream_.close_child_fds();
1311
1312 try {
1313 char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
1314
1315 FILE* err_fp = fdopen(err_rd_pipe, "r");
1316 if (!err_fp) {
1317 subprocess_close(err_rd_pipe);
1318 throw OSError("fdopen failed", errno);
1319 }
1320 int read_bytes = util::read_atmost_n(err_fp, err_buf, SP_MAX_ERR_BUF_SIZ);
1321 fclose(err_fp);
1322
1323 if (read_bytes || strlen(err_buf)) {
1324 // Call waitpid to reap the child process
1325 // waitpid suspends the calling process until the
1326 // child terminates.
1327 int retcode = wait();
1328
1329 // Throw whatever information we have about child failure
1330 throw CalledProcessError(err_buf, retcode);
1331 }
1332 } catch (std::exception& exp) {
1333 stream_.cleanup_fds();
1334 throw;
1335 }
1336
1337 }
1338 #endif
1339 }
1340
1341 namespace detail {
1342
1343 inline void ArgumentDeducer::set_option(executable&& exe) {
1344 popen_->exe_name_ = std::move(exe.arg_value);
1345 }
1346
1347 inline void ArgumentDeducer::set_option(input&& inp) {
1348 if (inp.rd_ch_ != -1) popen_->stream_.read_from_parent_ = inp.rd_ch_;
1349 if (inp.wr_ch_ != -1) popen_->stream_.write_to_child_ = inp.wr_ch_;
1350 }
1351
1352 inline void ArgumentDeducer::set_option(output&& out) {
1353 if (out.wr_ch_ != -1) popen_->stream_.write_to_parent_ = out.wr_ch_;
1354 if (out.rd_ch_ != -1) popen_->stream_.read_from_child_ = out.rd_ch_;
1355 }
1356
1357 inline void ArgumentDeducer::set_option(error&& err) {
1358 if (err.deferred_) {
1359 if (popen_->stream_.write_to_parent_) {
1360 popen_->stream_.err_write_ = popen_->stream_.write_to_parent_;
1361 } else {
1362 throw std::runtime_error("Set output before redirecting error to output");
1363 }
1364 }
1365 if (err.wr_ch_ != -1) popen_->stream_.err_write_ = err.wr_ch_;
1366 if (err.rd_ch_ != -1) popen_->stream_.err_read_ = err.rd_ch_;
1367 }
1368
1369 inline void ArgumentDeducer::set_option(close_fds&& cfds) {
1370 popen_->close_fds_ = cfds.close_all;
1371 }
1372
1373 #ifndef __USING_WINDOWS__
1374 void subprocess_close_all_fds(int except_fd);
1375 #endif
1376
1377
1378 inline void Child::execute_child() {
1379 #ifndef __USING_WINDOWS__
1380 int sys_ret = -1;
1381 auto& stream = parent_->stream_;
1382
1383 try {
1384 if (stream.write_to_parent_ == 0)
1385 stream.write_to_parent_ = dup(stream.write_to_parent_);
1386
1387 if (stream.err_write_ == 0 || stream.err_write_ == 1)
1388 stream.err_write_ = dup(stream.err_write_);
1389
1390 // Make the child owned descriptors as the
1391 // stdin, stdout and stderr for the child process
1392 auto _dup2_ = [](int fd, int to_fd) {
1393 if (fd == to_fd) {
1394 // dup2 syscall does not reset the
1395 // CLOEXEC flag if the descriptors
1396 // provided to it are same.
1397 // But, we need to reset the CLOEXEC
1398 // flag as the provided descriptors
1399 // are now going to be the standard
1400 // input, output and error
1401 util::set_clo_on_exec(fd, false);
1402 } else if(fd != -1) {
1403 int res = dup2(fd, to_fd);
1404 if (res == -1) throw OSError("dup2 failed", errno);
1405 }
1406 };
1407
1408 // Create the standard streams
1409 _dup2_(stream.read_from_parent_, 0); // Input stream
1410 _dup2_(stream.write_to_parent_, 1); // Output stream
1411 _dup2_(stream.err_write_, 2); // Error stream
1412
1413 // Close the duped descriptors
1414 if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2)
1415 subprocess_close(stream.read_from_parent_);
1416
1417 if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2)
1418 subprocess_close(stream.write_to_parent_);
1419
1420 if (stream.err_write_ != -1 && stream.err_write_ > 2)
1421 subprocess_close(stream.err_write_);
1422
1423 // Close all the inherited fd's except the error write pipe
1424 if (parent_->close_fds_) {
1425 subprocess_close_all_fds(/*except_fd=*/ err_wr_pipe_);
1426 }
1427
1428 // Replace the current image with the executable
1429 sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
1430
1431 if (sys_ret == -1) throw OSError("execve failed", errno);
1432
1433 } catch (const OSError& exp) {
1434 // Just write the exception message
1435 // TODO: Give back stack trace ?
1436 std::string err_msg(exp.what());
1437 //ATTN: Can we do something on error here ?
1438 util::write_n(err_wr_pipe_, err_msg.c_str(), err_msg.length());
1439 }
1440
1441 // Calling application would not get this
1442 // exit failure
1443 _exit (EXIT_FAILURE);
1444 #endif
1445 }
1446
1447
1448 inline void Streams::setup_comm_channels()
1449 {
1450 #ifdef __USING_WINDOWS__
1451 util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
1452 this->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w"));
1453 this->write_to_child_ = subprocess_fileno(this->input());
1454
1455 util::configure_pipe(&this->g_hChildStd_OUT_Rd, &this->g_hChildStd_OUT_Wr, &this->g_hChildStd_OUT_Rd);
1456 this->output(util::file_from_handle(this->g_hChildStd_OUT_Rd, "r"));
1457 this->read_from_child_ = subprocess_fileno(this->output());
1458
1459 util::configure_pipe(&this->g_hChildStd_ERR_Rd, &this->g_hChildStd_ERR_Wr, &this->g_hChildStd_ERR_Rd);
1460 this->error(util::file_from_handle(this->g_hChildStd_ERR_Rd, "r"));
1461 this->err_read_ = subprocess_fileno(this->error());
1462 #else
1463
1464 if (write_to_child_ != -1) input(fdopen(write_to_child_, "wb"));
1465 if (read_from_child_ != -1) output(fdopen(read_from_child_, "rb"));
1466 if (err_read_ != -1) error(fdopen(err_read_, "rb"));
1467
1468 auto handles = {input(), output(), error()};
1469
1470 for (auto& h : handles) {
1471 if (h == nullptr) continue;
1472 setvbuf(h, nullptr, _IONBF, BUFSIZ);
1473 }
1474 #endif
1475 }
1476
1477 inline int Communication::send(const char* msg, size_t length)
1478 {
1479 if (stream_->input() == nullptr) return -1;
1480 return std::fwrite(msg, sizeof(char), length, stream_->input());
1481 }
1482
1483 inline int Communication::send(const std::vector<char>& msg)
1484 {
1485 return send(msg.data(), msg.size());
1486 }
1487
1488 inline std::pair<OutBuffer, ErrBuffer>
1489 Communication::communicate(const char* msg, size_t length)
1490 {
1491 // Optimization from subprocess.py
1492 // If we are using one pipe, or no pipe
1493 // at all, using select() or threads is unnecessary.
1494 auto hndls = {stream_->input(), stream_->output(), stream_->error()};
1495 int count = std::count(std::begin(hndls), std::end(hndls), nullptr);
1496 const int len_conv = length;
1497
1498 if (count >= 2) {
1499 OutBuffer obuf;
1500 ErrBuffer ebuf;
1501 if (stream_->input()) {
1502 if (msg) {
1503 int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
1504 if (wbytes < len_conv) {
1505 if (errno != EPIPE && errno != EINVAL) {
1506 throw OSError("fwrite error", errno);
1507 }
1508 }
1509 }
1510 // Close the input stream
1511 stream_->input_.reset();
1512 } else if (stream_->output()) {
1513 // Read till EOF
1514 // ATTN: This could be blocking, if the process
1515 // at the other end screws up, we get screwed as well
1516 obuf.add_cap(out_buf_cap_);
1517
1518 int rbytes = util::read_all(
1519 stream_->output(),
1520 obuf.buf);
1521
1522 if (rbytes == -1) {
1523 throw OSError("read to obuf failed", errno);
1524 }
1525
1526 obuf.length = rbytes;
1527 // Close the output stream
1528 stream_->output_.reset();
1529
1530 } else if (stream_->error()) {
1531 // Same screwness applies here as well
1532 ebuf.add_cap(err_buf_cap_);
1533
1534 int rbytes = util::read_atmost_n(
1535 stream_->error(),
1536 ebuf.buf.data(),
1537 ebuf.buf.size());
1538
1539 if (rbytes == -1) {
1540 throw OSError("read to ebuf failed", errno);
1541 }
1542
1543 ebuf.length = rbytes;
1544 // Close the error stream
1545 stream_->error_.reset();
1546 }
1547 return std::make_pair(std::move(obuf), std::move(ebuf));
1548 }
1549
1550 return communicate_threaded(msg, length);
1551 }
1552
1553
1554 inline std::pair<OutBuffer, ErrBuffer>
1555 Communication::communicate_threaded(const char* msg, size_t length)
1556 {
1557 OutBuffer obuf;
1558 ErrBuffer ebuf;
1559 std::future<int> out_fut, err_fut;
1560 const int length_conv = length;
1561
1562 if (stream_->output()) {
1563 obuf.add_cap(out_buf_cap_);
1564
1565 out_fut = std::async(std::launch::async,
1566 [&obuf, this] {
1567 return util::read_all(this->stream_->output(), obuf.buf);
1568 });
1569 }
1570 if (stream_->error()) {
1571 ebuf.add_cap(err_buf_cap_);
1572
1573 err_fut = std::async(std::launch::async,
1574 [&ebuf, this] {
1575 return util::read_all(this->stream_->error(), ebuf.buf);
1576 });
1577 }
1578 if (stream_->input()) {
1579 if (msg) {
1580 int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
1581 if (wbytes < length_conv) {
1582 if (errno != EPIPE && errno != EINVAL) {
1583 throw OSError("fwrite error", errno);
1584 }
1585 }
1586 }
1587 stream_->input_.reset();
1588 }
1589
1590 if (out_fut.valid()) {
1591 int res = out_fut.get();
1592 if (res != -1) obuf.length = res;
1593 else obuf.length = 0;
1594 }
1595 if (err_fut.valid()) {
1596 int res = err_fut.get();
1597 if (res != -1) ebuf.length = res;
1598 else ebuf.length = 0;
1599 }
1600
1601 return std::make_pair(std::move(obuf), std::move(ebuf));
1602 }
1603
1604 } // end namespace detail
1605
1606 }
1607
1608 #endif // LIMENKA_UTIL_SUBPROCESS_H
1609