LCOV - code coverage report
Current view: top level - src/util - subprocess.h (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 64.3 % 336 216
Test Date: 2025-05-10 04:34:30 Functions: 83.9 % 31 26
Branches: 30.1 % 342 103

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

Generated by: LCOV version 2.0-1