LCOV - code coverage report
Current view: top level - src/util - subprocess.h (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 61.9 % 354 219
Test Date: 2025-05-31 05:07:57 Functions: 83.9 % 31 26
Branches: 26.7 % 390 104

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

Generated by: LCOV version 2.0-1