LCOV - code coverage report
Current view: top level - src/util - subprocess.hpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 265 442 60.0 %
Date: 2026-06-25 07:23:43 Functions: 71 85 83.5 %

          Line data    Source code
       1             : /*!
       2             : 
       3             : Documentation for C++ subprocessing libraray.
       4             : 
       5             : @copyright The code is licensed under the [MIT
       6             :   License](http://opensource.org/licenses/MIT):
       7             :   <br>
       8             :   Copyright &copy; 2016-2018 Arun Muralidharan.
       9             :   <br>
      10             :   Permission is hereby granted, free of charge, to any person obtaining a copy
      11             :   of this software and associated documentation files (the "Software"), to deal
      12             :   in the Software without restriction, including without limitation the rights
      13             :   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      14             :   copies of the Software, and to permit persons to whom the Software is
      15             :   furnished to do so, subject to the following conditions:
      16             :   <br>
      17             :   The above copyright notice and this permission notice shall be included in
      18             :   all copies or substantial portions of the Software.
      19             :   <br>
      20             :   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      21             :   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      23             :   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      25             :   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      26             :   SOFTWARE.
      27             : 
      28             : @author [Arun Muralidharan]
      29             : @see https://github.com/arun11299/cpp-subprocess to download the source code
      30             : 
      31             : @version 1.0.0
      32             : */
      33             : 
      34             : #ifndef SUBPROCESS_HPP
      35             : #define SUBPROCESS_HPP
      36             : 
      37             : #include <algorithm>
      38             : #include <cassert>
      39             : #include <csignal>
      40             : #include <cstdio>
      41             : #include <cstdlib>
      42             : #include <cstring>
      43             : #include <exception>
      44             : #include <future>
      45             : #include <initializer_list>
      46             : #include <iostream>
      47             : #include <locale>
      48             : #include <map>
      49             : #include <memory>
      50             : #include <sstream>
      51             : #include <string>
      52             : #include <vector>
      53             : 
      54             : #if (defined _MSC_VER) || (defined __MINGW32__)
      55             :   #define __USING_WINDOWS__
      56             : #endif
      57             : 
      58             : #ifdef __USING_WINDOWS__
      59             :   #include <codecvt>
      60             : #endif
      61             : 
      62             : extern "C" {
      63             : #ifdef __USING_WINDOWS__
      64             :   #include <Windows.h>
      65             :   #include <io.h>
      66             :   #include <cwchar>
      67             : 
      68             :   #define close _close
      69             :   #define open _open
      70             :   #define fileno _fileno
      71             : #else
      72             :   #include <sys/wait.h>
      73             :   #include <unistd.h>
      74             : #endif
      75             :   #include <csignal>
      76             :   #include <fcntl.h>
      77             :   #include <sys/types.h>
      78             : }
      79             : 
      80             : /*!
      81             :  * Getting started with reading this source code.
      82             :  * The source is mainly divided into four parts:
      83             :  * 1. Exception Classes:
      84             :  *    These are very basic exception classes derived from
      85             :  *    runtime_error exception.
      86             :  *    There are two types of exception thrown from subprocess
      87             :  *    library: OSError and CalledProcessError
      88             :  *
      89             :  * 2. Popen Class
      90             :  *    This is the main class the users will deal with. It
      91             :  *    provides with all the API's to deal with processes.
      92             :  *
      93             :  * 3. Util namespace
      94             :  *    It includes some helper functions to split/join a string,
      95             :  *    reading from file descriptors, waiting on a process, fcntl
      96             :  *    options on file descriptors etc.
      97             :  *
      98             :  * 4. Detail namespace
      99             :  *    This includes some metaprogramming and helper classes.
     100             :  */
     101             : 
     102             : 
     103             : namespace subprocess {
     104             : 
     105             : // Max buffer size allocated on stack for read error
     106             : // from pipe
     107             : static const size_t SP_MAX_ERR_BUF_SIZ = 1024;
     108             : 
     109             : // Default buffer capcity for OutBuffer and ErrBuffer.
     110             : // If the data exceeds this capacity, the buffer size is grown
     111             : // by 1.5 times its previous capacity
     112             : static const size_t DEFAULT_BUF_CAP_BYTES = 8192;
     113             : 
     114             : 
     115             : /*-----------------------------------------------
     116             :  *    EXCEPTION CLASSES
     117             :  *-----------------------------------------------
     118             :  */
     119             : 
     120             : /*!
     121             :  * class: CalledProcessError
     122             :  * Thrown when there was error executing the command.
     123             :  * Check Popen class API's to know when this exception
     124             :  * can be thrown.
     125             :  *
     126             :  */
     127             : class CalledProcessError: public std::runtime_error
     128             : {
     129             : public:
     130             :   int retcode;
     131           6 :   CalledProcessError(const std::string& error_msg, int retcode):
     132           6 :     std::runtime_error(error_msg), retcode(retcode)
     133           9 :   {}
     134             : };
     135             : 
     136             : 
     137             : /*!
     138             :  * class: OSError
     139             :  * Thrown when some system call fails to execute or give result.
     140             :  * The exception message contains the name of the failed system call
     141             :  * with the stringisized errno code.
     142             :  * Check Popen class API's to know when this exception would be
     143             :  * thrown.
     144             :  * Its usual that the API exception specification would have
     145             :  * this exception together with CalledProcessError.
     146             :  */
     147             : class OSError: public std::runtime_error
     148             : {
     149             : public:
     150           0 :   OSError(const std::string& err_msg, int err_code):
     151           0 :     std::runtime_error( err_msg + ": " + std::strerror(err_code) )
     152           0 :   {}
     153             : };
     154             : 
     155             : //--------------------------------------------------------------------
     156             : 
     157             : //Environment Variable types
     158             : #ifndef _MSC_VER
     159             :         using env_string_t = std::string;
     160             :         using env_char_t = char;
     161             : #else
     162             :         using env_string_t = std::wstring;
     163             :         using env_char_t = wchar_t;
     164             : #endif
     165             : using env_map_t = std::map<env_string_t, env_string_t>;
     166             : using env_vector_t = std::vector<env_char_t>;
     167             : 
     168             : //--------------------------------------------------------------------
     169             : namespace util
     170             : {
     171             :   template <typename R>
     172             :   inline bool is_ready(std::shared_future<R> const &f)
     173             :   {
     174             :     return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
     175             :   }
     176             : 
     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             : #ifdef __USING_WINDOWS__
     238             :   inline std::string get_last_error(DWORD errorMessageID)
     239             :   {
     240             :     if (errorMessageID == 0)
     241             :       return std::string();
     242             : 
     243             :     LPSTR messageBuffer = nullptr;
     244             :     size_t size = FormatMessageA(
     245             :         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
     246             :             FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
     247             :         NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
     248             :         (LPSTR)&messageBuffer, 0, NULL);
     249             : 
     250             :     std::string message(messageBuffer, size);
     251             : 
     252             :     LocalFree(messageBuffer);
     253             : 
     254             :     return message;
     255             :   }
     256             : 
     257             :   inline FILE *file_from_handle(HANDLE h, const char *mode)
     258             :   {
     259             :     int md;
     260             :     if (!mode) {
     261             :       throw OSError("invalid_mode", 0);
     262             :     }
     263             : 
     264             :     if (mode[0] == 'w') {
     265             :       md = _O_WRONLY;
     266             :     }
     267             :     else if (mode[0] == 'r') {
     268             :       md = _O_RDONLY;
     269             :     }
     270             :     else {
     271             :       throw OSError("file_from_handle", 0);
     272             :     }
     273             : 
     274             :     int os_fhandle = _open_osfhandle((intptr_t)h, md);
     275             :     if (os_fhandle == -1) {
     276             :       CloseHandle(h);
     277             :       throw OSError("_open_osfhandle", 0);
     278             :     }
     279             : 
     280             :     FILE *fp = _fdopen(os_fhandle, mode);
     281             :     if (fp == 0) {
     282             :       _close(os_fhandle);
     283             :       throw OSError("_fdopen", 0);
     284             :     }
     285             : 
     286             :     return fp;
     287             :   }
     288             : 
     289             :   inline void configure_pipe(HANDLE* read_handle, HANDLE* write_handle, HANDLE* child_handle)
     290             :   {
     291             :     SECURITY_ATTRIBUTES saAttr;
     292             : 
     293             :     // Set the bInheritHandle flag so pipe handles are inherited.
     294             :     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
     295             :     saAttr.bInheritHandle = TRUE;
     296             :     saAttr.lpSecurityDescriptor = NULL;
     297             : 
     298             :     // Create a pipe for the child process's STDIN.
     299             :     if (!CreatePipe(read_handle, write_handle, &saAttr,0))
     300             :       throw OSError("CreatePipe", 0);
     301             : 
     302             :     // Ensure the write handle to the pipe for STDIN is not inherited.
     303             :     if (!SetHandleInformation(*child_handle, HANDLE_FLAG_INHERIT, 0))
     304             :       throw OSError("SetHandleInformation", 0);
     305             :   }
     306             : 
     307             :   // env_map_t MapFromWindowsEnvironment()
     308             :   // * Imports current Environment in a C-string table
     309             :   // * Parses the strings by splitting on the first "=" per line
     310             :   // * Creates a map of the variables
     311             :   // * Returns the map
     312             :   inline env_map_t MapFromWindowsEnvironment(){
     313             :       wchar_t *variable_strings_ptr;
     314             :       wchar_t *environment_strings_ptr;
     315             :       std::wstring delimeter(L"=");
     316             :       int del_len = delimeter.length();
     317             :       env_map_t mapped_environment;
     318             : 
     319             :       // Get a pointer to the environment block.
     320             :       environment_strings_ptr = GetEnvironmentStringsW();
     321             :       // If the returned pointer is NULL, exit.
     322             :       if (environment_strings_ptr == NULL)
     323             :       {
     324             :           throw OSError("GetEnvironmentStringsW", 0);
     325             :       }
     326             : 
     327             :       // Variable strings are separated by NULL byte, and the block is
     328             :       // terminated by a NULL byte.
     329             : 
     330             :       variable_strings_ptr = (wchar_t *) environment_strings_ptr;
     331             : 
     332             :       //Since the environment map ends with a null, we can loop until we find it.
     333             :       while (*variable_strings_ptr)
     334             :       {
     335             :           // Create a string from Variable String
     336             :           env_string_t current_line(variable_strings_ptr);
     337             :           // Find the first "equals" sign.
     338             :           auto pos = current_line.find(delimeter);
     339             :           // Assuming it's not missing ...
     340             :           if(pos!=std::wstring::npos){
     341             :               // ... parse the key and value.
     342             :               env_string_t key = current_line.substr(0, pos);
     343             :               env_string_t value = current_line.substr(pos + del_len);
     344             :               // Map the entry.
     345             :               mapped_environment[key] = value;
     346             :           }
     347             :           // Jump to next line in the environment map.
     348             :           variable_strings_ptr += std::wcslen(variable_strings_ptr) + 1;
     349             :       }
     350             :       // We're done with the old environment map buffer.
     351             :       FreeEnvironmentStringsW(environment_strings_ptr);
     352             : 
     353             :       // Return the map.
     354             :       return mapped_environment;
     355             :   }
     356             : 
     357             :   // env_vector_t WindowsEnvironmentVectorFromMap(const env_map_t &source_map)
     358             :   // * Creates a vector buffer for the new environment string table
     359             :   // * Copies in the mapped variables
     360             :   // * Returns the vector
     361             :   inline env_vector_t WindowsEnvironmentVectorFromMap(const env_map_t &source_map)
     362             :   {
     363             :         // Make a new environment map buffer.
     364             :         env_vector_t environment_map_buffer;
     365             :         // Give it some space.
     366             :         environment_map_buffer.reserve(4096);
     367             : 
     368             :         // And fill'er up.
     369             :         for(auto kv: source_map){
     370             :           // Create the line
     371             :           env_string_t current_line(kv.first); current_line += L"="; current_line += kv.second;
     372             :           // Add the line to the buffer.
     373             :           std::copy(current_line.begin(), current_line.end(), std::back_inserter(environment_map_buffer));
     374             :           // Append a null
     375             :           environment_map_buffer.push_back(0);
     376             :         }
     377             :         // Append one last null because of how Windows does it's environment maps.
     378             :         environment_map_buffer.push_back(0);
     379             : 
     380             :         return environment_map_buffer;
     381             :   }
     382             : 
     383             :   // env_vector_t CreateUpdatedWindowsEnvironmentVector(const env_map_t &changes_map)
     384             :   // * Merges host environment with new mapped variables
     385             :   // * Creates and returns string vector based on map
     386             :   inline env_vector_t CreateUpdatedWindowsEnvironmentVector(const env_map_t &changes_map){
     387             :         // Import the environment map
     388             :         env_map_t environment_map = MapFromWindowsEnvironment();
     389             :     // Merge in the changes with overwrite
     390             :         for(auto& it: changes_map)
     391             :         {
     392             :                 environment_map[it.first] = it.second;
     393             :         }
     394             :     // Create a Windows-usable Environment Map Buffer
     395             :     env_vector_t environment_map_strings_vector = WindowsEnvironmentVectorFromMap(environment_map);
     396             : 
     397             :         return environment_map_strings_vector;
     398             :   }
     399             : 
     400             : #endif
     401             : 
     402             :   /*!
     403             :    * Function: split
     404             :    * Parameters:
     405             :    * [in] str : Input string which needs to be split based upon the
     406             :    *            delimiters provided.
     407             :    * [in] deleims : Delimiter characters based upon which the string needs
     408             :    *                to be split. Default constructed to ' '(space) and '\t'(tab)
     409             :    * [out] vector<string> : Vector of strings split at deleimiter.
     410             :    */
     411             :   static inline std::vector<std::string>
     412          38 :   split(const std::string& str, const std::string& delims=" \t")
     413             :   {
     414          38 :     std::vector<std::string> res;
     415          38 :     size_t init = 0;
     416             : 
     417         126 :     while (true) {
     418         126 :       auto pos = str.find_first_of(delims, init);
     419         126 :       if (pos == std::string::npos) {
     420          38 :         res.emplace_back(str.substr(init, str.length()));
     421          38 :         break;
     422             :       }
     423          88 :       res.emplace_back(str.substr(init, pos - init));
     424          88 :       pos++;
     425          88 :       init = pos;
     426             :     }
     427             : 
     428          38 :     return res;
     429          38 :   }
     430             : 
     431             : 
     432             :   /*!
     433             :    * Function: join
     434             :    * Parameters:
     435             :    * [in] vec : Vector of strings which needs to be joined to form
     436             :    *            a single string with words seperated by a seperator char.
     437             :    *  [in] sep : String used to seperate 2 words in the joined string.
     438             :    *             Default constructed to ' ' (space).
     439             :    *  [out] string: Joined string.
     440             :    */
     441             :   static inline
     442           0 :   std::string join(const std::vector<std::string>& vec,
     443             :                    const std::string& sep = " ")
     444             :   {
     445           0 :     std::string res;
     446           0 :     for (auto& elem : vec) res.append(elem + sep);
     447           0 :     res.erase(--res.end());
     448           0 :     return res;
     449           0 :   }
     450             : 
     451             : 
     452             : #ifndef __USING_WINDOWS__
     453             :   /*!
     454             :    * Function: set_clo_on_exec
     455             :    * Sets/Resets the FD_CLOEXEC flag on the provided file descriptor
     456             :    * based upon the `set` parameter.
     457             :    * Parameters:
     458             :    * [in] fd : The descriptor on which FD_CLOEXEC needs to be set/reset.
     459             :    * [in] set : If 'true', set FD_CLOEXEC.
     460             :    *            If 'false' unset FD_CLOEXEC.
     461             :    */
     462             :   static inline
     463         304 :   void set_clo_on_exec(int fd, bool set = true)
     464             :   {
     465         304 :     int flags = fcntl(fd, F_GETFD, 0);
     466         304 :     if (set) flags |= FD_CLOEXEC;
     467           0 :     else flags &= ~FD_CLOEXEC;
     468             :     //TODO: should check for errors
     469         304 :     fcntl(fd, F_SETFD, flags);
     470         304 :   }
     471             : 
     472             : 
     473             :   /*!
     474             :    * Function: pipe_cloexec
     475             :    * Creates a pipe and sets FD_CLOEXEC flag on both
     476             :    * read and write descriptors of the pipe.
     477             :    * Parameters:
     478             :    * [out] : A pair of file descriptors.
     479             :    *         First element of pair is the read descriptor of pipe.
     480             :    *         Second element is the write descriptor of pipe.
     481             :    */
     482             :   static inline
     483         152 :   std::pair<int, int> pipe_cloexec() noexcept(false)
     484             :   {
     485             :     int pipe_fds[2];
     486         152 :     int res = pipe(pipe_fds);
     487         152 :     if (res) {
     488           0 :       throw OSError("pipe failure", errno);
     489             :     }
     490             : 
     491         152 :     set_clo_on_exec(pipe_fds[0]);
     492         152 :     set_clo_on_exec(pipe_fds[1]);
     493             : 
     494         152 :     return std::make_pair(pipe_fds[0], pipe_fds[1]);
     495           0 :   }
     496             : #endif
     497             : 
     498             : 
     499             :   /*!
     500             :    * Function: write_n
     501             :    * Writes `length` bytes to the file descriptor `fd`
     502             :    * from the buffer `buf`.
     503             :    * Parameters:
     504             :    * [in] fd : The file descriptotr to write to.
     505             :    * [in] buf: Buffer from which data needs to be written to fd.
     506             :    * [in] length: The number of bytes that needs to be written from
     507             :    *              `buf` to `fd`.
     508             :    * [out] int : Number of bytes written or -1 in case of failure.
     509             :    */
     510             :   static inline
     511           0 :   int write_n(int fd, const char* buf, size_t length)
     512             :   {
     513           0 :     size_t nwritten = 0;
     514           0 :     while (nwritten < length) {
     515           0 :       int written = write(fd, buf + nwritten, length - nwritten);
     516           0 :       if (written == -1) return -1;
     517           0 :       nwritten += written;
     518             :     }
     519           0 :     return nwritten;
     520           0 :   }
     521             : 
     522             : 
     523             :   /*!
     524             :    * Function: read_atmost_n
     525             :    * Reads at the most `read_upto` bytes from the
     526             :    * file object `fp` before returning.
     527             :    * Parameters:
     528             :    * [in] fp : The file object from which it needs to read.
     529             :    * [in] buf : The buffer into which it needs to write the data.
     530             :    * [in] read_upto: Max number of bytes which must be read from `fd`.
     531             :    * [out] int : Number of bytes written to `buf` or read from `fd`
     532             :    *             OR -1 in case of error.
     533             :    *  NOTE: In case of EINTR while reading from socket, this API
     534             :    *  will retry to read from `fd`, but only till the EINTR counter
     535             :    *  reaches 50 after which it will return with whatever data it read.
     536             :    */
     537             :   static inline
     538         108 :   int read_atmost_n(FILE* fp, char* buf, size_t read_upto)
     539             :   {
     540             : #ifdef __USING_WINDOWS__
     541             :     return (int)fread(buf, 1, read_upto, fp);
     542             : #else
     543         108 :     int fd = fileno(fp);
     544         108 :     int rbytes = 0;
     545         108 :     int eintr_cnter = 0;
     546             : 
     547         141 :     while (1) {
     548         141 :       int read_bytes = read(fd, buf + rbytes, read_upto - rbytes);
     549         141 :       if (read_bytes == -1) {
     550           0 :         if (errno == EINTR) {
     551           0 :           if (eintr_cnter >= 50) return -1;
     552           0 :           eintr_cnter++;
     553           0 :           continue;
     554             :         }
     555           0 :         return -1;
     556             :       }
     557         126 :       if (read_bytes == 0) return rbytes;
     558             : 
     559          33 :       rbytes += read_bytes;
     560             :     }
     561             :     return rbytes;
     562             : #endif
     563          93 :   }
     564             : 
     565             : 
     566             :   /*!
     567             :    * Function: read_all
     568             :    * Reads all the available data from `fp` into
     569             :    * `buf`. Internally calls read_atmost_n.
     570             :    * Parameters:
     571             :    * [in] fp : The file object from which to read from.
     572             :    * [in] buf : The buffer of type `class Buffer` into which
     573             :    *            the read data is written to.
     574             :    * [out] int: Number of bytes read OR -1 in case of failure.
     575             :    *
     576             :    * NOTE: `class Buffer` is a exposed public class. See below.
     577             :    */
     578             : 
     579          70 :   static inline int read_all(FILE* fp, std::vector<char>& buf)
     580             :   {
     581          70 :     auto buffer = buf.data();
     582          70 :     int total_bytes_read = 0;
     583          70 :     int fill_sz = buf.size();
     584             : 
     585          70 :     while (1) {
     586          70 :       const int rd_bytes = read_atmost_n(fp, buffer, fill_sz);
     587             : 
     588          70 :       if (rd_bytes == -1) { // Read finished
     589           0 :         if (total_bytes_read == 0) return -1;
     590           0 :         break;
     591             : 
     592          65 :       } else if (rd_bytes == fill_sz) { // Buffer full
     593           0 :         const auto orig_sz = buf.size();
     594           0 :         const auto new_sz = orig_sz * 2;
     595           0 :         buf.resize(new_sz);
     596           0 :         fill_sz = new_sz - orig_sz;
     597             : 
     598             :         //update the buffer pointer
     599           0 :         buffer = buf.data();
     600           0 :         total_bytes_read += rd_bytes;
     601           0 :         buffer += total_bytes_read;
     602             : 
     603           0 :       } else { // Partial data ? Continue reading
     604          66 :         total_bytes_read += rd_bytes;
     605          66 :         fill_sz -= rd_bytes;
     606          66 :         break;
     607             :       }
     608             :     }
     609          59 :     buf.erase(buf.begin()+total_bytes_read, buf.end()); // remove extra nulls
     610          59 :     return total_bytes_read;
     611          59 :   }
     612             : 
     613             : #ifndef __USING_WINDOWS__
     614             :   /*!
     615             :    * Function: wait_for_child_exit
     616             :    * Waits for the process with pid `pid` to exit
     617             :    * and returns its status.
     618             :    * Parameters:
     619             :    * [in] pid : The pid of the process.
     620             :    * [out] pair<int, int>:
     621             :    *    pair.first : Return code of the waitpid call.
     622             :    *    pair.second : Exit status of the process.
     623             :    *
     624             :    *  NOTE: This is a blocking call as in, it will loop
     625             :    *  till the child is exited.
     626             :    */
     627             :   static inline
     628          38 :   std::pair<int, int> wait_for_child_exit(int pid)
     629             :   {
     630          38 :     int status = 0;
     631          38 :     int ret = -1;
     632          38 :     while (1) {
     633          38 :       ret = waitpid(pid, &status, 0);
     634          38 :       if (ret == -1) break;
     635          38 :       if (ret == 0) continue;
     636          38 :       return std::make_pair(ret, status);
     637             :     }
     638             : 
     639           0 :     return std::make_pair(ret, status);
     640          38 :   }
     641             : #endif
     642             : 
     643             : } // end namespace util
     644             : 
     645             : 
     646             : 
     647             : /* -------------------------------
     648             :  *     Popen Arguments
     649             :  * -------------------------------
     650             :  */
     651             : 
     652             : /*!
     653             :  * The buffer size of the stdin/stdout/stderr
     654             :  * streams of the child process.
     655             :  * Default value is 0.
     656             :  */
     657             : struct bufsize {
     658             :   explicit bufsize(int siz): bufsiz(siz) {}
     659             :   int  bufsiz = 0;
     660             : };
     661             : 
     662             : /*!
     663             :  * Option to defer spawning of the child process
     664             :  * till `Popen::start_process` API is called.
     665             :  * Default value is false.
     666             :  */
     667             : struct defer_spawn {
     668             :   explicit defer_spawn(bool d): defer(d) {}
     669             :   bool defer  = false;
     670             : };
     671             : 
     672             : /*!
     673             :  * Option to close all file descriptors
     674             :  * when the child process is spawned.
     675             :  * The close fd list does not include
     676             :  * input/output/error if they are explicitly
     677             :  * set as part of the Popen arguments.
     678             :  *
     679             :  * Default value is false.
     680             :  */
     681             : struct close_fds {
     682             :   explicit close_fds(bool c): close_all(c) {}
     683             :   bool close_all = false;
     684             : };
     685             : 
     686             : /*!
     687             :  * Option to make the child process as the
     688             :  * session leader and thus the process
     689             :  * group leader.
     690             :  * Default value is false.
     691             :  */
     692             : struct session_leader {
     693             :   explicit session_leader(bool sl): leader_(sl) {}
     694             :   bool leader_ = false;
     695             : };
     696             : 
     697             : struct shell {
     698             :   explicit shell(bool s): shell_(s) {}
     699             :   bool shell_ = false;
     700             : };
     701             : 
     702             : /*!
     703             :  * Base class for all arguments involving string value.
     704             :  */
     705             : struct string_arg
     706             : {
     707             :   string_arg(const char* arg): arg_value(arg) {}
     708             :   string_arg(std::string&& arg): arg_value(std::move(arg)) {}
     709             :   string_arg(std::string arg): arg_value(std::move(arg)) {}
     710             :   std::string arg_value;
     711             : };
     712             : 
     713             : /*!
     714             :  * Option to specify the executable name seperately
     715             :  * from the args sequence.
     716             :  * In this case the cmd args must only contain the
     717             :  * options required for this executable.
     718             :  *
     719             :  * Eg: executable{"ls"}
     720             :  */
     721             : struct executable: string_arg
     722             : {
     723             :   template <typename T>
     724             :   executable(T&& arg): string_arg(std::forward<T>(arg)) {}
     725             : };
     726             : 
     727             : /*!
     728             :  * Option to set the current working directory
     729             :  * of the spawned process.
     730             :  *
     731             :  * Eg: cwd{"/som/path/x"}
     732             :  */
     733             : struct cwd: string_arg
     734             : {
     735             :   template <typename T>
     736             :   cwd(T&& arg): string_arg(std::forward<T>(arg)) {}
     737             : };
     738             : 
     739             : /*!
     740             :  * Option to specify environment variables required by
     741             :  * the spawned process.
     742             :  *
     743             :  * Eg: environment{{ {"K1", "V1"}, {"K2", "V2"},... }}
     744             :  */
     745             : struct environment
     746             : {
     747             :   environment(env_map_t&& env):
     748             :     env_(std::move(env)) {}
     749             :   explicit environment(const env_map_t& env):
     750             :     env_(env) {}
     751             :   env_map_t env_;
     752             : };
     753             : 
     754             : 
     755             : /*!
     756             :  * Used for redirecting input/output/error
     757             :  */
     758             : enum IOTYPE {
     759             :   STDOUT = 1,
     760             :   STDERR,
     761             :   PIPE,
     762             : };
     763             : 
     764             : //TODO: A common base/interface for below stream structures ??
     765             : 
     766             : /*!
     767             :  * Option to specify the input channel for the child
     768             :  * process. It can be:
     769             :  * 1. An already open file descriptor.
     770             :  * 2. A file name.
     771             :  * 3. IOTYPE. Usual a PIPE
     772             :  *
     773             :  * Eg: input{PIPE}
     774             :  * OR in case of redirection, output of another Popen
     775             :  * input{popen.output()}
     776             :  */
     777             : struct input
     778             : {
     779             :   // For an already existing file descriptor.
     780             :   explicit input(int fd): rd_ch_(fd) {}
     781             : 
     782             :   // FILE pointer.
     783             :   explicit input (FILE* fp):input(fileno(fp)) { assert(fp); }
     784             : 
     785             :   explicit input(const char* filename) {
     786             :     int fd = open(filename, O_RDONLY);
     787             :     if (fd == -1) throw OSError("File not found: ", errno);
     788             :     rd_ch_ = fd;
     789             :   }
     790          76 :   explicit input(IOTYPE typ) {
     791          76 :     assert (typ == PIPE && "STDOUT/STDERR not allowed");
     792             : #ifndef __USING_WINDOWS__
     793          38 :     std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
     794             : #endif
     795          76 :   }
     796             : 
     797          38 :   int rd_ch_ = -1;
     798          38 :   int wr_ch_ = -1;
     799             : };
     800             : 
     801             : 
     802             : /*!
     803             :  * Option to specify the output channel for the child
     804             :  * process. It can be:
     805             :  * 1. An already open file descriptor.
     806             :  * 2. A file name.
     807             :  * 3. IOTYPE. Usually a PIPE.
     808             :  *
     809             :  * Eg: output{PIPE}
     810             :  * OR output{"output.txt"}
     811             :  */
     812             : struct output
     813             : {
     814             :   explicit output(int fd): wr_ch_(fd) {}
     815             : 
     816             :   explicit output (FILE* fp):output(fileno(fp)) { assert(fp); }
     817             : 
     818             :   explicit output(const char* filename) {
     819             :     int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
     820             :     if (fd == -1) throw OSError("File not found: ", errno);
     821             :     wr_ch_ = fd;
     822             :   }
     823          76 :   explicit output(IOTYPE typ) {
     824          76 :     assert (typ == PIPE && "STDOUT/STDERR not allowed");
     825             : #ifndef __USING_WINDOWS__
     826          38 :     std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
     827             : #endif
     828          76 :   }
     829             : 
     830          38 :   int rd_ch_ = -1;
     831          38 :   int wr_ch_ = -1;
     832             : };
     833             : 
     834             : 
     835             : /*!
     836             :  * Option to specify the error channel for the child
     837             :  * process. It can be:
     838             :  * 1. An already open file descriptor.
     839             :  * 2. A file name.
     840             :  * 3. IOTYPE. Usually a PIPE or STDOUT
     841             :  *
     842             :  */
     843             : struct error
     844             : {
     845             :   explicit error(int fd): wr_ch_(fd) {}
     846             : 
     847             :   explicit error(FILE* fp):error(fileno(fp)) { assert(fp); }
     848             : 
     849             :   explicit error(const char* filename) {
     850             :     int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
     851             :     if (fd == -1) throw OSError("File not found: ", errno);
     852             :     wr_ch_ = fd;
     853             :   }
     854          76 :   explicit error(IOTYPE typ) {
     855          76 :     assert ((typ == PIPE || typ == STDOUT) && "STDERR not aloowed");
     856          38 :     if (typ == PIPE) {
     857             : #ifndef __USING_WINDOWS__
     858          38 :       std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
     859             : #endif
     860          38 :     } else {
     861             :       // Need to defer it till we have checked all arguments
     862           0 :       deferred_ = true;
     863             :     }
     864          76 :   }
     865             : 
     866          38 :   bool deferred_ = false;
     867          38 :   int rd_ch_ = -1;
     868          38 :   int wr_ch_ = -1;
     869             : };
     870             : 
     871             : // Impoverished, meager, needy, truly needy
     872             : // version of type erasure to store function pointers
     873             : // needed to provide the functionality of preexec_func
     874             : // ATTN: Can be used only to execute functions with no
     875             : // arguments and returning void.
     876             : // Could have used more efficient methods, ofcourse, but
     877             : // that wont yield me the consistent syntax which I am
     878             : // aiming for. If you know, then please do let me know.
     879             : 
     880             : class preexec_func
     881             : {
     882             : public:
     883         114 :   preexec_func() {}
     884             : 
     885             :   template <typename Func>
     886             :   explicit preexec_func(Func f): holder_(new FuncHolder<Func>(std::move(f)))
     887             :   {}
     888             : 
     889           0 :   void operator()() {
     890           0 :     (*holder_)();
     891           0 :   }
     892             : 
     893             : private:
     894             :   struct HolderBase {
     895             :     virtual void operator()() const = 0;
     896             :     virtual ~HolderBase(){};
     897             :   };
     898             :   template <typename T>
     899             :   struct FuncHolder: HolderBase {
     900             :     FuncHolder(T func): func_(std::move(func)) {}
     901             :     void operator()() const override { func_(); }
     902             :     // The function pointer/reference
     903             :     T func_;
     904             :   };
     905             : 
     906          38 :   std::unique_ptr<HolderBase> holder_ = nullptr;
     907             : };
     908             : 
     909             : // ~~~~ End Popen Args ~~~~
     910             : 
     911             : 
     912             : /*!
     913             :  * class: Buffer
     914             :  * This class is a very thin wrapper around std::vector<char>
     915             :  * This is basically used to determine the length of the actual
     916             :  * data stored inside the dynamically resized vector.
     917             :  *
     918             :  * This is what is returned as the output to communicate and check_output
     919             :  * functions, so, users must know about this class.
     920             :  *
     921             :  * OutBuffer and ErrBuffer are just different typedefs to this class.
     922             :  */
     923             : class Buffer
     924             : {
     925             : public:
     926         210 :   Buffer() {}
     927             :   explicit Buffer(size_t cap) { buf.resize(cap); }
     928          70 :   void add_cap(size_t cap) { buf.resize(cap); }
     929             : 
     930             : #if 0
     931             :   Buffer(const Buffer& other):
     932             :     buf(other.buf),
     933             :     length(other.length)
     934             :   {
     935             :     std::cout << "COPY" << std::endl;
     936             :   }
     937             : 
     938             :   Buffer(Buffer&& other):
     939             :     buf(std::move(other.buf)),
     940             :     length(other.length)
     941             :   {
     942             :     std::cout << "MOVE" << std::endl;
     943             :   }
     944             : #endif
     945             : 
     946             : public:
     947             :   std::vector<char> buf;
     948          70 :   size_t length = 0;
     949             : };
     950             : 
     951             : // Buffer for storing output written to output fd
     952             : using OutBuffer = Buffer;
     953             : // Buffer for storing output written to error fd
     954             : using ErrBuffer = Buffer;
     955             : 
     956             : 
     957             : // Fwd Decl.
     958             : class Popen;
     959             : 
     960             : /*---------------------------------------------------
     961             :  *      DETAIL NAMESPACE
     962             :  *---------------------------------------------------
     963             :  */
     964             : 
     965             : namespace detail {
     966             : 
     967             : // Metaprogram for searching a type within
     968             : // a variadic parameter pack
     969             : // This is particularly required to do a compile time
     970             : // checking of the arguments provided to 'check_ouput' function
     971             : // wherein the user is not expected to provide an 'ouput' option.
     972             : 
     973             : template <typename... T> struct param_pack{};
     974             : 
     975             : template <typename F, typename T> struct has_type;
     976             : 
     977             : template <typename F>
     978             : struct has_type<F, param_pack<>> {
     979             :   static constexpr bool value = false;
     980             : };
     981             : 
     982             : template <typename F, typename... T>
     983             : struct has_type<F, param_pack<F, T...>> {
     984             :   static constexpr bool value = true;
     985             : };
     986             : 
     987             : template <typename F, typename H, typename... T>
     988             : struct has_type<F, param_pack<H,T...>> {
     989             :   static constexpr bool value =
     990             :     std::is_same<F, typename std::decay<H>::type>::value ? true : has_type<F, param_pack<T...>>::value;
     991             : };
     992             : 
     993             : //----
     994             : 
     995             : /*!
     996             :  * A helper class to Popen class for setting
     997             :  * options as provided in the Popen constructor
     998             :  * or in check_ouput arguments.
     999             :  * This design allows us to _not_ have any fixed position
    1000             :  * to any arguments and specify them in a way similar to what
    1001             :  * can be done in python.
    1002             :  */
    1003             : struct ArgumentDeducer
    1004             : {
    1005         228 :   ArgumentDeducer(Popen* p): popen_(p) {}
    1006             : 
    1007             :   void set_option(executable&& exe);
    1008             :   void set_option(cwd&& cwdir);
    1009             :   void set_option(bufsize&& bsiz);
    1010             :   void set_option(environment&& env);
    1011             :   void set_option(defer_spawn&& defer);
    1012             :   void set_option(shell&& sh);
    1013             :   void set_option(input&& inp);
    1014             :   void set_option(output&& out);
    1015             :   void set_option(error&& err);
    1016             :   void set_option(close_fds&& cfds);
    1017             :   void set_option(preexec_func&& prefunc);
    1018             :   void set_option(session_leader&& sleader);
    1019             : 
    1020             : private:
    1021             :   Popen* popen_ = nullptr;
    1022             : };
    1023             : 
    1024             : /*!
    1025             :  * A helper class to Popen.
    1026             :  * This takes care of all the fork-exec logic
    1027             :  * in the execute_child API.
    1028             :  */
    1029             : class Child
    1030             : {
    1031             : public:
    1032           0 :   Child(Popen* p, int err_wr_pipe):
    1033           0 :     parent_(p),
    1034           0 :     err_wr_pipe_(err_wr_pipe)
    1035           0 :   {}
    1036             : 
    1037             :   void execute_child();
    1038             : 
    1039             : private:
    1040             :   // Lets call it parent even though
    1041             :   // technically a bit incorrect
    1042             :   Popen* parent_ = nullptr;
    1043             :   int err_wr_pipe_ = -1;
    1044             : };
    1045             : 
    1046             : // Fwd Decl.
    1047             : class Streams;
    1048             : 
    1049             : /*!
    1050             :  * A helper class to Streams.
    1051             :  * This takes care of management of communicating
    1052             :  * with the child process with the means of the correct
    1053             :  * file descriptor.
    1054             :  */
    1055             : class Communication
    1056             : {
    1057             : public:
    1058          76 :   Communication(Streams* stream): stream_(stream)
    1059          76 :   {}
    1060             :   void operator=(const Communication&) = delete;
    1061             : public:
    1062             :   int send(const char* msg, size_t length);
    1063             :   int send(const std::vector<char>& msg);
    1064             : 
    1065             :   std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length);
    1066             :   std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
    1067             :   { return communicate(msg.data(), msg.size()); }
    1068             : 
    1069             :   void set_out_buf_cap(size_t cap) { out_buf_cap_ = cap; }
    1070             :   void set_err_buf_cap(size_t cap) { err_buf_cap_ = cap; }
    1071             : 
    1072             : private:
    1073             :   std::pair<OutBuffer, ErrBuffer> communicate_threaded(
    1074             :       const char* msg, size_t length);
    1075             : 
    1076             : private:
    1077             :   Streams* stream_;
    1078          38 :   size_t out_buf_cap_ = DEFAULT_BUF_CAP_BYTES;
    1079          38 :   size_t err_buf_cap_ = DEFAULT_BUF_CAP_BYTES;
    1080             : };
    1081             : 
    1082             : 
    1083             : 
    1084             : /*!
    1085             :  * This is a helper class to Popen.
    1086             :  * It takes care of management of all the file descriptors
    1087             :  * and file pointers.
    1088             :  * It dispatches of the communication aspects to the
    1089             :  * Communication class.
    1090             :  * Read through the data members to understand about the
    1091             :  * various file descriptors used.
    1092             :  */
    1093             : class Streams
    1094             : {
    1095             : public:
    1096         114 :   Streams():comm_(this) {}
    1097             :   void operator=(const Streams&) = delete;
    1098             : 
    1099             : public:
    1100             :   void setup_comm_channels();
    1101             : 
    1102           3 :   void cleanup_fds()
    1103             :   {
    1104           3 :     if (write_to_child_ != -1 && read_from_parent_ != -1) {
    1105           3 :       close(write_to_child_);
    1106           3 :     }
    1107           3 :     if (write_to_parent_ != -1 && read_from_child_ != -1) {
    1108           3 :       close(read_from_child_);
    1109           3 :     }
    1110           3 :     if (err_write_ != -1 && err_read_ != -1) {
    1111           3 :       close(err_read_);
    1112           3 :     }
    1113           3 :   }
    1114             : 
    1115           0 :   void close_parent_fds()
    1116             :   {
    1117           0 :     if (write_to_child_ != -1)  close(write_to_child_);
    1118           0 :     if (read_from_child_ != -1) close(read_from_child_);
    1119           0 :     if (err_read_ != -1)        close(err_read_);
    1120           0 :   }
    1121             : 
    1122          38 :   void close_child_fds()
    1123             :   {
    1124          38 :     if (write_to_parent_ != -1)  close(write_to_parent_);
    1125          38 :     if (read_from_parent_ != -1) close(read_from_parent_);
    1126          38 :     if (err_write_ != -1)        close(err_write_);
    1127          38 :   }
    1128             : 
    1129         118 :   FILE* input()  { return input_.get(); }
    1130         143 :   FILE* output() { return output_.get(); }
    1131         143 :   FILE* error()  { return error_.get(); }
    1132             : 
    1133          38 :   void input(FILE* fp)  { input_.reset(fp, fclose); }
    1134          38 :   void output(FILE* fp) { output_.reset(fp, fclose); }
    1135          38 :   void error(FILE* fp)  { error_.reset(fp, fclose); }
    1136             : 
    1137             :   void set_out_buf_cap(size_t cap) { comm_.set_out_buf_cap(cap); }
    1138             :   void set_err_buf_cap(size_t cap) { comm_.set_err_buf_cap(cap); }
    1139             : 
    1140             : public: /* Communication forwarding API's */
    1141           5 :   int send(const char* msg, size_t length)
    1142           5 :   { return comm_.send(msg, length); }
    1143             : 
    1144             :   int send(const std::vector<char>& msg)
    1145             :   { return comm_.send(msg); }
    1146             : 
    1147          35 :   std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
    1148          35 :   { return comm_.communicate(msg, length); }
    1149             : 
    1150             :   std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
    1151             :   { return comm_.communicate(msg); }
    1152             : 
    1153             : 
    1154             : public:// Yes they are public
    1155             : 
    1156          38 :   std::shared_ptr<FILE> input_  = nullptr;
    1157          38 :   std::shared_ptr<FILE> output_ = nullptr;
    1158          38 :   std::shared_ptr<FILE> error_  = nullptr;
    1159             : 
    1160             : #ifdef __USING_WINDOWS__
    1161             :   HANDLE g_hChildStd_IN_Rd = nullptr;
    1162             :   HANDLE g_hChildStd_IN_Wr = nullptr;
    1163             :   HANDLE g_hChildStd_OUT_Rd = nullptr;
    1164             :   HANDLE g_hChildStd_OUT_Wr = nullptr;
    1165             :   HANDLE g_hChildStd_ERR_Rd = nullptr;
    1166             :   HANDLE g_hChildStd_ERR_Wr = nullptr;
    1167             : #endif
    1168             : 
    1169             :   // Buffer size for the IO streams
    1170          38 :   int bufsiz_ = 0;
    1171             : 
    1172             :   // Pipes for communicating with child
    1173             : 
    1174             :   // Emulates stdin
    1175          38 :   int write_to_child_   = -1; // Parent owned descriptor
    1176          38 :   int read_from_parent_ = -1; // Child owned descriptor
    1177             : 
    1178             :   // Emulates stdout
    1179          38 :   int write_to_parent_ = -1; // Child owned descriptor
    1180          38 :   int read_from_child_ = -1; // Parent owned descriptor
    1181             : 
    1182             :   // Emulates stderr
    1183          38 :   int err_write_ = -1; // Write error to parent (Child owned)
    1184          38 :   int err_read_  = -1; // Read error from child (Parent owned)
    1185             : 
    1186             : private:
    1187             :   Communication comm_;
    1188             : };
    1189             : 
    1190             : } // end namespace detail
    1191             : 
    1192             : 
    1193             : 
    1194             : /*!
    1195             :  * class: Popen
    1196             :  * This is the single most important class in the whole library
    1197             :  * and glues together all the helper classes to provide a common
    1198             :  * interface to the client.
    1199             :  *
    1200             :  * API's provided by the class:
    1201             :  * 1. Popen({"cmd"}, output{..}, error{..}, cwd{..}, ....)
    1202             :  *    Command provided as a sequence.
    1203             :  * 2. Popen("cmd arg1"m output{..}, error{..}, cwd{..}, ....)
    1204             :  *    Command provided in a single string.
    1205             :  * 3. wait()             - Wait for the child to exit.
    1206             :  * 4. retcode()          - The return code of the exited child.
    1207             :  * 5. pid()              - PID of the spawned child.
    1208             :  * 6. poll()             - Check the status of the running child.
    1209             :  * 7. kill(sig_num)      - Kill the child. SIGTERM used by default.
    1210             :  * 8. send(...)          - Send input to the input channel of the child.
    1211             :  * 9. communicate(...)   - Get the output/error from the child and close the channels
    1212             :  *                         from the parent side.
    1213             :  *10. input()            - Get the input channel/File pointer. Can be used for
    1214             :  *                         cutomizing the way of sending input to child.
    1215             :  *11. output()           - Get the output channel/File pointer. Usually used
    1216             :                            in case of redirection. See piping examples.
    1217             :  *12. error()            - Get the error channel/File poiner. Usually used
    1218             :                            in case of redirection.
    1219             :  *13. start_process()    - Start the child process. Only to be used when
    1220             :  *                         `defer_spawn` option was provided in Popen constructor.
    1221             :  */
    1222             : class Popen
    1223             : {
    1224             : public:
    1225             :   friend struct detail::ArgumentDeducer;
    1226             :   friend class detail::Child;
    1227             : 
    1228             :   template <typename... Args>
    1229         152 :   Popen(const std::string& cmd_args, Args&& ...args):
    1230          38 :     args_(cmd_args)
    1231          38 :   {
    1232          38 :     vargs_ = util::split(cmd_args);
    1233          38 :     init_args(std::forward<Args>(args)...);
    1234             : 
    1235             :     // Setup the communication channels of the Popen class
    1236          38 :     stream_.setup_comm_channels();
    1237             : 
    1238          38 :     if (!defer_process_start_) execute_process();
    1239          76 :   }
    1240             : 
    1241             :   template <typename... Args>
    1242             :   Popen(std::initializer_list<const char*> cmd_args, Args&& ...args)
    1243             :   {
    1244             :     vargs_.insert(vargs_.end(), cmd_args.begin(), cmd_args.end());
    1245             :     init_args(std::forward<Args>(args)...);
    1246             : 
    1247             :     // Setup the communication channels of the Popen class
    1248             :     stream_.setup_comm_channels();
    1249             : 
    1250             :     if (!defer_process_start_) execute_process();
    1251             :   }
    1252             : 
    1253             :   template <typename... Args>
    1254             :   Popen(std::vector<std::string> vargs_, Args &&... args) : vargs_(vargs_)
    1255             :   {
    1256             :     init_args(std::forward<Args>(args)...);
    1257             : 
    1258             :     // Setup the communication channels of the Popen class
    1259             :     stream_.setup_comm_channels();
    1260             : 
    1261             :     if (!defer_process_start_) execute_process();
    1262             :   }
    1263             : 
    1264             : /*
    1265             :   ~Popen()
    1266             :   {
    1267             : #ifdef __USING_WINDOWS__
    1268             :     CloseHandle(this->process_handle_);
    1269             : #endif
    1270             :   }
    1271             : */
    1272             : 
    1273             :   void start_process() noexcept(false);
    1274             : 
    1275          38 :   int pid() const noexcept { return child_pid_; }
    1276             : 
    1277          35 :   int retcode() const noexcept { return retcode_; }
    1278             : 
    1279             :   int wait() noexcept(false);
    1280             : 
    1281             :   int poll() noexcept(false);
    1282             : 
    1283             :   // Does not fail, Caller is expected to recheck the
    1284             :   // status with a call to poll()
    1285             :   void kill(int sig_num = 9);
    1286             : 
    1287             :   void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
    1288             : 
    1289             :   void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
    1290             : 
    1291           5 :   int send(const char* msg, size_t length)
    1292           5 :   { return stream_.send(msg, length); }
    1293             : 
    1294           5 :   int send(const std::string& msg)
    1295           5 :   { return send(msg.c_str(), msg.size()); }
    1296             : 
    1297             :   int send(const std::vector<char>& msg)
    1298             :   { return stream_.send(msg); }
    1299             : 
    1300          35 :   std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
    1301             :   {
    1302          35 :     auto res = stream_.communicate(msg, length);
    1303          35 :     retcode_ = wait();
    1304          35 :     return res;
    1305          35 :   }
    1306             : 
    1307             :   std::pair<OutBuffer, ErrBuffer> communicate(const std::string& msg)
    1308             :   {
    1309             :     return communicate(msg.c_str(), msg.size());
    1310             :   }
    1311             : 
    1312             :   std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
    1313             :   {
    1314             :     auto res = stream_.communicate(msg);
    1315             :     retcode_ = wait();
    1316             :     return res;
    1317             :   }
    1318             : 
    1319          35 :   std::pair<OutBuffer, ErrBuffer> communicate()
    1320             :   {
    1321          35 :     return communicate(nullptr, 0);
    1322             :   }
    1323             : 
    1324             :   FILE* input()  { return stream_.input(); }
    1325             :   FILE* output() { return stream_.output();}
    1326             :   FILE* error()  { return stream_.error(); }
    1327             : 
    1328             :   /// Stream close APIs
    1329             :   void close_input()  { stream_.input_.reset();  }
    1330             :   void close_output() { stream_.output_.reset(); }
    1331             :   void close_error()  { stream_.error_.reset();  }
    1332             : 
    1333             : private:
    1334             :   template <typename F, typename... Args>
    1335             :   void init_args(F&& farg, Args&&... args);
    1336             :   void init_args();
    1337             :   void populate_c_argv();
    1338             :   void execute_process() noexcept(false);
    1339             : 
    1340             : private:
    1341             :   detail::Streams stream_;
    1342             : 
    1343             : #ifdef __USING_WINDOWS__
    1344             :   HANDLE process_handle_;
    1345             :   std::future<void> cleanup_future_;
    1346             : #endif
    1347             : 
    1348          38 :   bool defer_process_start_ = false;
    1349          38 :   bool close_fds_ = false;
    1350          38 :   bool has_preexec_fn_ = false;
    1351          38 :   bool shell_ = false;
    1352          38 :   bool session_leader_ = false;
    1353             : 
    1354             :   std::string exe_name_;
    1355             :   std::string cwd_;
    1356             :   env_map_t env_;
    1357             :   preexec_func preexec_fn_;
    1358             : 
    1359             :   // Command in string format
    1360             :   std::string args_;
    1361             :   // Comamnd provided as sequence
    1362             :   std::vector<std::string> vargs_;
    1363             :   std::vector<char*> cargv_;
    1364             : 
    1365          38 :   bool child_created_ = false;
    1366             :   // Pid of the child process
    1367          38 :   int child_pid_ = -1;
    1368             : 
    1369          38 :   int retcode_ = -1;
    1370             : };
    1371             : 
    1372          38 : inline void Popen::init_args() {
    1373          38 :   populate_c_argv();
    1374          38 : }
    1375             : 
    1376             : template <typename F, typename... Args>
    1377         114 : inline void Popen::init_args(F&& farg, Args&&... args)
    1378             : {
    1379         114 :   detail::ArgumentDeducer argd(this);
    1380         114 :   argd.set_option(std::forward<F>(farg));
    1381         114 :   init_args(std::forward<Args>(args)...);
    1382         114 : }
    1383             : 
    1384          38 : inline void Popen::populate_c_argv()
    1385             : {
    1386          38 :   cargv_.clear();
    1387          38 :   cargv_.reserve(vargs_.size() + 1);
    1388         164 :   for (auto& arg : vargs_) cargv_.push_back(&arg[0]);
    1389          38 :   cargv_.push_back(nullptr);
    1390          38 : }
    1391             : 
    1392             : inline void Popen::start_process() noexcept(false)
    1393             : {
    1394             :   // The process was started/tried to be started
    1395             :   // in the constructor itself.
    1396             :   // For explicitly calling this API to start the
    1397             :   // process, 'defer_spawn' argument must be set to
    1398             :   // true in the constructor.
    1399             :   if (!defer_process_start_) {
    1400             :     assert (0);
    1401             :     return;
    1402             :   }
    1403             :   execute_process();
    1404             : }
    1405             : 
    1406          38 : inline int Popen::wait() noexcept(false)
    1407             : {
    1408             : #ifdef __USING_WINDOWS__
    1409             :   int ret = WaitForSingleObject(process_handle_, INFINITE);
    1410             : 
    1411             :   return 0;
    1412             : #else
    1413             :   int ret, status;
    1414          38 :   std::tie(ret, status) = util::wait_for_child_exit(pid());
    1415          38 :   if (ret == -1) {
    1416           0 :     if (errno != ECHILD) throw OSError("waitpid failed", errno);
    1417           0 :     return 0;
    1418             :   }
    1419          38 :   if (WIFEXITED(status)) return WEXITSTATUS(status);
    1420           0 :   if (WIFSIGNALED(status)) return WTERMSIG(status);
    1421           0 :   else return 255;
    1422             : 
    1423             :   return 0;
    1424             : #endif
    1425          38 : }
    1426             : 
    1427             : inline int Popen::poll() noexcept(false)
    1428             : {
    1429             : #ifdef __USING_WINDOWS__
    1430             :   int ret = WaitForSingleObject(process_handle_, 0);
    1431             :   if (ret != WAIT_OBJECT_0) return -1;
    1432             : 
    1433             :   DWORD dretcode_;
    1434             :   if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
    1435             :       throw OSError("GetExitCodeProcess", 0);
    1436             : 
    1437             :   retcode_ = (int)dretcode_;
    1438             :   CloseHandle(process_handle_);
    1439             : 
    1440             :   return retcode_;
    1441             : #else
    1442             :   if (!child_created_) return -1; // TODO: ??
    1443             : 
    1444             :   int status;
    1445             : 
    1446             :   // Returns zero if child is still running
    1447             :   int ret = waitpid(child_pid_, &status, WNOHANG);
    1448             :   if (ret == 0) return -1;
    1449             : 
    1450             :   if (ret == child_pid_) {
    1451             :     if (WIFSIGNALED(status)) {
    1452             :       retcode_ = WTERMSIG(status);
    1453             :     } else if (WIFEXITED(status)) {
    1454             :       retcode_ = WEXITSTATUS(status);
    1455             :     } else {
    1456             :       retcode_ = 255;
    1457             :     }
    1458             :     return retcode_;
    1459             :   }
    1460             : 
    1461             :   if (ret == -1) {
    1462             :     // From subprocess.py
    1463             :     // This happens if SIGCHLD is set to be ignored
    1464             :     // or waiting for child process has otherwise been disabled
    1465             :     // for our process. This child is dead, we cannot get the
    1466             :     // status.
    1467             :     if (errno == ECHILD) retcode_ = 0;
    1468             :     else throw OSError("waitpid failed", errno);
    1469             :   } else {
    1470             :     retcode_ = ret;
    1471             :   }
    1472             : 
    1473             :   return retcode_;
    1474             : #endif
    1475             : }
    1476             : 
    1477             : inline void Popen::kill(int sig_num)
    1478             : {
    1479             : #ifdef __USING_WINDOWS__
    1480             :   if (!TerminateProcess(this->process_handle_, (UINT)sig_num)) {
    1481             :     throw OSError("TerminateProcess", 0);
    1482             :   }
    1483             : #else
    1484             :   if (session_leader_) killpg(child_pid_, sig_num);
    1485             :   else ::kill(child_pid_, sig_num);
    1486             : #endif
    1487             : }
    1488             : 
    1489             : 
    1490          38 : inline void Popen::execute_process() noexcept(false)
    1491             : {
    1492             : #ifdef __USING_WINDOWS__
    1493             :   if (this->shell_) {
    1494             :     throw OSError("shell not currently supported on windows", 0);
    1495             :   }
    1496             : 
    1497             :   void* environment_string_table_ptr = nullptr;
    1498             :   env_vector_t environment_string_vector;
    1499             :   if(this->env_.size()){
    1500             :           environment_string_vector = util::CreateUpdatedWindowsEnvironmentVector(env_);
    1501             :           environment_string_table_ptr = (void*)environment_string_vector.data();
    1502             :   }
    1503             : 
    1504             :   if (exe_name_.length()) {
    1505             :     this->vargs_.insert(this->vargs_.begin(), this->exe_name_);
    1506             :     this->populate_c_argv();
    1507             :   }
    1508             :   this->exe_name_ = vargs_[0];
    1509             : 
    1510             :   std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
    1511             :   std::wstring argument;
    1512             :   std::wstring command_line;
    1513             : 
    1514             :   for (auto arg : this->vargs_) {
    1515             :     argument = converter.from_bytes(arg);
    1516             :     util::quote_argument(argument, command_line, false);
    1517             :     command_line += L" ";
    1518             :   }
    1519             : 
    1520             :   // CreateProcessW can modify szCmdLine so we allocate needed memory
    1521             :   wchar_t *szCmdline = new wchar_t[command_line.size() + 1];
    1522             :   wcscpy_s(szCmdline, command_line.size() + 1, command_line.c_str());
    1523             :   PROCESS_INFORMATION piProcInfo;
    1524             :   STARTUPINFOW siStartInfo;
    1525             :   BOOL bSuccess = FALSE;
    1526             :   DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW;
    1527             : 
    1528             :   // Set up members of the PROCESS_INFORMATION structure.
    1529             :   ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    1530             : 
    1531             :   // Set up members of the STARTUPINFOW structure.
    1532             :   // This structure specifies the STDIN and STDOUT handles for redirection.
    1533             : 
    1534             :   ZeroMemory(&siStartInfo, sizeof(STARTUPINFOW));
    1535             :   siStartInfo.cb = sizeof(STARTUPINFOW);
    1536             : 
    1537             :   siStartInfo.hStdError = this->stream_.g_hChildStd_ERR_Wr;
    1538             :   siStartInfo.hStdOutput = this->stream_.g_hChildStd_OUT_Wr;
    1539             :   siStartInfo.hStdInput = this->stream_.g_hChildStd_IN_Rd;
    1540             : 
    1541             :   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
    1542             : 
    1543             :   // Create the child process.
    1544             :   bSuccess = CreateProcessW(NULL,
    1545             :                             szCmdline,    // command line
    1546             :                             NULL,         // process security attributes
    1547             :                             NULL,         // primary thread security attributes
    1548             :                             TRUE,         // handles are inherited
    1549             :                             creation_flags,     // creation flags
    1550             :                             environment_string_table_ptr,  // use provided environment
    1551             :                             NULL,         // use parent's current directory
    1552             :                             &siStartInfo, // STARTUPINFOW pointer
    1553             :                             &piProcInfo); // receives PROCESS_INFORMATION
    1554             : 
    1555             :   // If an error occurs, exit the application.
    1556             :   if (!bSuccess) {
    1557             :     DWORD errorMessageID = ::GetLastError();
    1558             :     throw CalledProcessError("CreateProcess failed: " + util::get_last_error(errorMessageID), errorMessageID);
    1559             :   }
    1560             : 
    1561             :   CloseHandle(piProcInfo.hThread);
    1562             : 
    1563             :   /*
    1564             :     TODO: use common apis to close linux handles
    1565             :   */
    1566             : 
    1567             :   this->process_handle_ = piProcInfo.hProcess;
    1568             : 
    1569             :   this->cleanup_future_ = std::async(std::launch::async, [this] {
    1570             :     WaitForSingleObject(this->process_handle_, INFINITE);
    1571             : 
    1572             :     CloseHandle(this->stream_.g_hChildStd_ERR_Wr);
    1573             :     CloseHandle(this->stream_.g_hChildStd_OUT_Wr);
    1574             :     CloseHandle(this->stream_.g_hChildStd_IN_Rd);
    1575             :   });
    1576             : 
    1577             : /*
    1578             :   NOTE: In the linux version, there is a check to make sure that the process
    1579             :         has been started. Here, we do nothing because CreateProcess will throw
    1580             :         if we fail to create the process.
    1581             : */
    1582             : 
    1583             : 
    1584             : #else
    1585             : 
    1586             :   int err_rd_pipe, err_wr_pipe;
    1587          38 :   std::tie(err_rd_pipe, err_wr_pipe) = util::pipe_cloexec();
    1588             : 
    1589          38 :   if (shell_) {
    1590           3 :     auto new_cmd = util::join(vargs_);
    1591           0 :     vargs_.clear();
    1592           0 :     vargs_.insert(vargs_.begin(), {"/bin/sh", "-c"});
    1593           0 :     vargs_.push_back(new_cmd);
    1594           0 :     populate_c_argv();
    1595           0 :   }
    1596             : 
    1597           0 :   if (exe_name_.length()) {
    1598           0 :     vargs_.insert(vargs_.begin(), exe_name_);
    1599           0 :     populate_c_argv();
    1600           0 :   }
    1601          38 :   exe_name_ = vargs_[0];
    1602             : 
    1603          38 :   child_pid_ = fork();
    1604             : 
    1605          38 :   if (child_pid_ < 0) {
    1606           0 :     close(err_rd_pipe);
    1607           0 :     close(err_wr_pipe);
    1608           0 :     throw OSError("fork failed", errno);
    1609             :   }
    1610             : 
    1611          38 :   child_created_ = true;
    1612             : 
    1613          38 :   if (child_pid_ == 0)
    1614             :   {
    1615             :     // Close descriptors belonging to parent
    1616           0 :     stream_.close_parent_fds();
    1617             : 
    1618             :     //Close the read end of the error pipe
    1619           0 :     close(err_rd_pipe);
    1620             : 
    1621           0 :     detail::Child chld(this, err_wr_pipe);
    1622           0 :     chld.execute_child();
    1623           0 :   }
    1624             :   else
    1625             :   {
    1626          38 :     close (err_wr_pipe);// close child side of pipe, else get stuck in read below
    1627             : 
    1628          38 :     stream_.close_child_fds();
    1629             : 
    1630             :     try {
    1631          38 :       char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
    1632             : 
    1633          38 :       int read_bytes = util::read_atmost_n(
    1634          38 :                                   fdopen(err_rd_pipe, "r"),
    1635          38 :                                   err_buf,
    1636             :                                   SP_MAX_ERR_BUF_SIZ);
    1637          38 :       close(err_rd_pipe);
    1638             : 
    1639          38 :       if (read_bytes || strlen(err_buf)) {
    1640             :         // Call waitpid to reap the child process
    1641             :         // waitpid suspends the calling process until the
    1642             :         // child terminates.
    1643           3 :         int retcode = wait();
    1644             : 
    1645             :         // Throw whatever information we have about child failure
    1646           3 :         throw CalledProcessError(err_buf, retcode);
    1647             :       }
    1648          38 :     } catch (std::exception& exp) {
    1649           3 :       stream_.cleanup_fds();
    1650           3 :       throw;
    1651           0 :     }
    1652             : 
    1653             :   }
    1654             : #endif
    1655          35 : }
    1656             : 
    1657             : namespace detail {
    1658             : 
    1659             :   inline void ArgumentDeducer::set_option(executable&& exe) {
    1660             :     popen_->exe_name_ = std::move(exe.arg_value);
    1661             :   }
    1662             : 
    1663             :   inline void ArgumentDeducer::set_option(cwd&& cwdir) {
    1664             :     popen_->cwd_ = std::move(cwdir.arg_value);
    1665             :   }
    1666             : 
    1667             :   inline void ArgumentDeducer::set_option(bufsize&& bsiz) {
    1668             :     popen_->stream_.bufsiz_ = bsiz.bufsiz;
    1669             :   }
    1670             : 
    1671             :   inline void ArgumentDeducer::set_option(environment&& env) {
    1672             :     popen_->env_ = std::move(env.env_);
    1673             :   }
    1674             : 
    1675             :   inline void ArgumentDeducer::set_option(defer_spawn&& defer) {
    1676             :     popen_->defer_process_start_ = defer.defer;
    1677             :   }
    1678             : 
    1679             :   inline void ArgumentDeducer::set_option(shell&& sh) {
    1680             :     popen_->shell_ = sh.shell_;
    1681             :   }
    1682             : 
    1683             :   inline void ArgumentDeducer::set_option(session_leader&& sleader) {
    1684             :     popen_->session_leader_ = sleader.leader_;
    1685             :   }
    1686             : 
    1687          38 :   inline void ArgumentDeducer::set_option(input&& inp) {
    1688          38 :     if (inp.rd_ch_ != -1) popen_->stream_.read_from_parent_ = inp.rd_ch_;
    1689          38 :     if (inp.wr_ch_ != -1) popen_->stream_.write_to_child_ = inp.wr_ch_;
    1690          38 :   }
    1691             : 
    1692          38 :   inline void ArgumentDeducer::set_option(output&& out) {
    1693          38 :     if (out.wr_ch_ != -1) popen_->stream_.write_to_parent_ = out.wr_ch_;
    1694          38 :     if (out.rd_ch_ != -1) popen_->stream_.read_from_child_ = out.rd_ch_;
    1695          38 :   }
    1696             : 
    1697          38 :   inline void ArgumentDeducer::set_option(error&& err) {
    1698          38 :     if (err.deferred_) {
    1699           0 :       if (popen_->stream_.write_to_parent_) {
    1700           0 :         popen_->stream_.err_write_ = popen_->stream_.write_to_parent_;
    1701           0 :       } else {
    1702           0 :         throw std::runtime_error("Set output before redirecting error to output");
    1703             :       }
    1704           0 :     }
    1705          38 :     if (err.wr_ch_ != -1) popen_->stream_.err_write_ = err.wr_ch_;
    1706          38 :     if (err.rd_ch_ != -1) popen_->stream_.err_read_ = err.rd_ch_;
    1707          38 :   }
    1708             : 
    1709             :   inline void ArgumentDeducer::set_option(close_fds&& cfds) {
    1710             :     popen_->close_fds_ = cfds.close_all;
    1711             :   }
    1712             : 
    1713             :   inline void ArgumentDeducer::set_option(preexec_func&& prefunc) {
    1714             :     popen_->preexec_fn_ = std::move(prefunc);
    1715             :     popen_->has_preexec_fn_ = true;
    1716             :   }
    1717             : 
    1718             : 
    1719           0 :   inline void Child::execute_child() {
    1720             : #ifndef __USING_WINDOWS__
    1721           0 :     int sys_ret = -1;
    1722           0 :     auto& stream = parent_->stream_;
    1723             : 
    1724             :     try {
    1725           0 :       if (stream.write_to_parent_ == 0)
    1726           0 :         stream.write_to_parent_ = dup(stream.write_to_parent_);
    1727             : 
    1728           0 :       if (stream.err_write_ == 0 || stream.err_write_ == 1)
    1729           0 :         stream.err_write_ = dup(stream.err_write_);
    1730             : 
    1731             :       // Make the child owned descriptors as the
    1732             :       // stdin, stdout and stderr for the child process
    1733           0 :       auto _dup2_ = [](int fd, int to_fd) {
    1734           0 :         if (fd == to_fd) {
    1735             :           // dup2 syscall does not reset the
    1736             :           // CLOEXEC flag if the descriptors
    1737             :           // provided to it are same.
    1738             :           // But, we need to reset the CLOEXEC
    1739             :           // flag as the provided descriptors
    1740             :           // are now going to be the standard
    1741             :           // input, output and error
    1742           0 :           util::set_clo_on_exec(fd, false);
    1743           0 :         } else if(fd != -1) {
    1744           0 :           int res = dup2(fd, to_fd);
    1745           0 :           if (res == -1) throw OSError("dup2 failed", errno);
    1746           0 :         }
    1747           0 :       };
    1748             : 
    1749             :       // Create the standard streams
    1750           0 :       _dup2_(stream.read_from_parent_, 0); // Input stream
    1751           0 :       _dup2_(stream.write_to_parent_,  1); // Output stream
    1752           0 :       _dup2_(stream.err_write_,        2); // Error stream
    1753             : 
    1754             :       // Close the duped descriptors
    1755           0 :       if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2)
    1756           0 :         close(stream.read_from_parent_);
    1757             : 
    1758           0 :       if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2)
    1759           0 :         close(stream.write_to_parent_);
    1760             : 
    1761           0 :       if (stream.err_write_ != -1 && stream.err_write_ > 2)
    1762           0 :         close(stream.err_write_);
    1763             : 
    1764             :       // Close all the inherited fd's except the error write pipe
    1765           0 :       if (parent_->close_fds_) {
    1766           0 :         int max_fd = sysconf(_SC_OPEN_MAX);
    1767           0 :         if (max_fd == -1) throw OSError("sysconf failed", errno);
    1768             : 
    1769           0 :         for (int i = 3; i < max_fd; i++) {
    1770           0 :           if (i == err_wr_pipe_) continue;
    1771           0 :           close(i);
    1772           0 :         }
    1773           0 :       }
    1774             : 
    1775             :       // Change the working directory if provided
    1776           0 :       if (parent_->cwd_.length()) {
    1777           0 :         sys_ret = chdir(parent_->cwd_.c_str());
    1778           0 :         if (sys_ret == -1) throw OSError("chdir failed", errno);
    1779           0 :       }
    1780             : 
    1781           0 :       if (parent_->has_preexec_fn_) {
    1782           0 :         parent_->preexec_fn_();
    1783           0 :       }
    1784             : 
    1785           0 :       if (parent_->session_leader_) {
    1786           0 :         sys_ret = setsid();
    1787           0 :         if (sys_ret == -1) throw OSError("setsid failed", errno);
    1788           0 :       }
    1789             : 
    1790             :       // Replace the current image with the executable
    1791           0 :       if (parent_->env_.size()) {
    1792           0 :         for (auto& kv : parent_->env_) {
    1793           0 :           setenv(kv.first.c_str(), kv.second.c_str(), 1);
    1794             :         }
    1795           0 :         sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
    1796           0 :       } else {
    1797           0 :         sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
    1798             :       }
    1799             : 
    1800           0 :       if (sys_ret == -1) throw OSError("execve failed", errno);
    1801             : 
    1802           0 :     } catch (const OSError& exp) {
    1803             :       // Just write the exception message
    1804             :       // TODO: Give back stack trace ?
    1805           0 :       std::string err_msg(exp.what());
    1806             :       //ATTN: Can we do something on error here ?
    1807           0 :       util::write_n(err_wr_pipe_, err_msg.c_str(), err_msg.length());
    1808           0 :     }
    1809             : 
    1810             :     // Calling application would not get this
    1811             :     // exit failure
    1812           0 :     _exit (EXIT_FAILURE);
    1813             : #endif
    1814           0 :   }
    1815             : 
    1816             : 
    1817          38 :   inline void Streams::setup_comm_channels()
    1818             :   {
    1819             : #ifdef __USING_WINDOWS__
    1820             :     util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
    1821             :     this->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w"));
    1822             :     this->write_to_child_ = _fileno(this->input());
    1823             : 
    1824             :     util::configure_pipe(&this->g_hChildStd_OUT_Rd, &this->g_hChildStd_OUT_Wr, &this->g_hChildStd_OUT_Rd);
    1825             :     this->output(util::file_from_handle(this->g_hChildStd_OUT_Rd, "r"));
    1826             :     this->read_from_child_ = _fileno(this->output());
    1827             : 
    1828             :     util::configure_pipe(&this->g_hChildStd_ERR_Rd, &this->g_hChildStd_ERR_Wr, &this->g_hChildStd_ERR_Rd);
    1829             :     this->error(util::file_from_handle(this->g_hChildStd_ERR_Rd, "r"));
    1830             :     this->err_read_ = _fileno(this->error());
    1831             : #else
    1832             : 
    1833          38 :     if (write_to_child_ != -1)  input(fdopen(write_to_child_, "wb"));
    1834          38 :     if (read_from_child_ != -1) output(fdopen(read_from_child_, "rb"));
    1835          38 :     if (err_read_ != -1)        error(fdopen(err_read_, "rb"));
    1836             : 
    1837          38 :     auto handles = {input(), output(), error()};
    1838             : 
    1839         152 :     for (auto& h : handles) {
    1840         114 :       if (h == nullptr) continue;
    1841         114 :       switch (bufsiz_) {
    1842             :       case 0:
    1843         114 :         setvbuf(h, nullptr, _IONBF, BUFSIZ);
    1844         114 :         break;
    1845             :       case 1:
    1846           0 :         setvbuf(h, nullptr, _IONBF, BUFSIZ);
    1847           0 :         break;
    1848             :       default:
    1849           0 :         setvbuf(h, nullptr, _IOFBF, bufsiz_);
    1850           0 :       };
    1851             :     }
    1852             :   #endif
    1853          38 :   }
    1854             : 
    1855           5 :   inline int Communication::send(const char* msg, size_t length)
    1856             :   {
    1857           5 :     if (stream_->input() == nullptr) return -1;
    1858           5 :     return std::fwrite(msg, sizeof(char), length, stream_->input());
    1859           5 :   }
    1860             : 
    1861             :   inline int Communication::send(const std::vector<char>& msg)
    1862             :   {
    1863             :     return send(msg.data(), msg.size());
    1864             :   }
    1865             : 
    1866             :   inline std::pair<OutBuffer, ErrBuffer>
    1867          35 :   Communication::communicate(const char* msg, size_t length)
    1868             :   {
    1869             :     // Optimization from subprocess.py
    1870             :     // If we are using one pipe, or no pipe
    1871             :     // at all, using select() or threads is unnecessary.
    1872          35 :     auto hndls = {stream_->input(), stream_->output(), stream_->error()};
    1873          35 :     int count = std::count(std::begin(hndls), std::end(hndls), nullptr);
    1874          35 :     const int len_conv = length;
    1875             : 
    1876          35 :     if (count >= 2) {
    1877           0 :       OutBuffer obuf;
    1878           0 :       ErrBuffer ebuf;
    1879           0 :       if (stream_->input()) {
    1880           0 :         if (msg) {
    1881           0 :           int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
    1882           0 :           if (wbytes < len_conv) {
    1883           0 :             if (errno != EPIPE && errno != EINVAL) {
    1884           0 :               throw OSError("fwrite error", errno);
    1885             :             }
    1886           0 :           }
    1887           0 :         }
    1888             :         // Close the input stream
    1889           0 :         stream_->input_.reset();
    1890           0 :       } else if (stream_->output()) {
    1891             :         // Read till EOF
    1892             :         // ATTN: This could be blocking, if the process
    1893             :         // at the other end screws up, we get screwed as well
    1894           0 :         obuf.add_cap(out_buf_cap_);
    1895             : 
    1896           0 :         int rbytes = util::read_all(
    1897           0 :                             stream_->output(),
    1898           0 :                             obuf.buf);
    1899             : 
    1900           0 :         if (rbytes == -1) {
    1901           0 :           throw OSError("read to obuf failed", errno);
    1902             :         }
    1903             : 
    1904           0 :         obuf.length = rbytes;
    1905             :         // Close the output stream
    1906           0 :         stream_->output_.reset();
    1907             : 
    1908           0 :       } else if (stream_->error()) {
    1909             :         // Same screwness applies here as well
    1910           0 :         ebuf.add_cap(err_buf_cap_);
    1911             : 
    1912           0 :         int rbytes = util::read_atmost_n(
    1913           0 :                                   stream_->error(),
    1914           0 :                                   ebuf.buf.data(),
    1915           0 :                                   ebuf.buf.size());
    1916             : 
    1917           0 :         if (rbytes == -1) {
    1918           0 :           throw OSError("read to ebuf failed", errno);
    1919             :         }
    1920             : 
    1921           0 :         ebuf.length = rbytes;
    1922             :         // Close the error stream
    1923           0 :         stream_->error_.reset();
    1924           0 :       }
    1925           0 :       return std::make_pair(std::move(obuf), std::move(ebuf));
    1926           0 :     }
    1927             : 
    1928          35 :     return communicate_threaded(msg, length);
    1929          35 :   }
    1930             : 
    1931             : 
    1932             :   inline std::pair<OutBuffer, ErrBuffer>
    1933          35 :   Communication::communicate_threaded(const char* msg, size_t length)
    1934             :   {
    1935          35 :     OutBuffer obuf;
    1936          35 :     ErrBuffer ebuf;
    1937          35 :     std::future<int> out_fut, err_fut;
    1938          35 :     const int length_conv = length;
    1939             : 
    1940          35 :     if (stream_->output()) {
    1941          35 :       obuf.add_cap(out_buf_cap_);
    1942             : 
    1943          35 :       out_fut = std::async(std::launch::async,
    1944          70 :                           [&obuf, this] {
    1945          35 :                             return util::read_all(this->stream_->output(), obuf.buf);
    1946             :                           });
    1947          35 :     }
    1948          35 :     if (stream_->error()) {
    1949          35 :       ebuf.add_cap(err_buf_cap_);
    1950             : 
    1951          35 :       err_fut = std::async(std::launch::async,
    1952          70 :                           [&ebuf, this] {
    1953          35 :                             return util::read_all(this->stream_->error(), ebuf.buf);
    1954             :                           });
    1955          35 :     }
    1956          35 :     if (stream_->input()) {
    1957          35 :       if (msg) {
    1958           0 :         int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
    1959           0 :         if (wbytes < length_conv) {
    1960           0 :           if (errno != EPIPE && errno != EINVAL) {
    1961           0 :             throw OSError("fwrite error", errno);
    1962             :           }
    1963           0 :         }
    1964           0 :       }
    1965          35 :       stream_->input_.reset();
    1966          35 :     }
    1967             : 
    1968          35 :     if (out_fut.valid()) {
    1969          35 :       int res = out_fut.get();
    1970          35 :       if (res != -1) obuf.length = res;
    1971           0 :       else obuf.length = 0;
    1972          35 :     }
    1973          35 :     if (err_fut.valid()) {
    1974          35 :       int res = err_fut.get();
    1975          35 :       if (res != -1) ebuf.length = res;
    1976           0 :       else ebuf.length = 0;
    1977          35 :     }
    1978             : 
    1979          35 :     return std::make_pair(std::move(obuf), std::move(ebuf));
    1980          35 :   }
    1981             : 
    1982             : } // end namespace detail
    1983             : 
    1984             : }
    1985             : 
    1986             : #endif // SUBPROCESS_HPP

Generated by: LCOV version 1.16