LCOV - code coverage report
Current view: top level - src/util - subprocess.h (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 64.1 % 334 214
Test Date: 2026-11-13 07:04:31 Functions: 83.9 % 31 26
Branches: 30.2 % 354 107

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

Generated by: LCOV version 2.0-1