LCOV - code coverage report
Current view: top level - src/util - subprocess.h (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 64.7 % 329 213
Test Date: 2024-11-04 05:10:19 Functions: 83.9 % 31 26
Branches: 31.1 % 322 100

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

Generated by: LCOV version 2.0-1