Skip to content

Commit 53ccb75

Browse files
committed
Merge bitcoin#32358: subprocess: Backport upstream changes
cd95c9d subprocess: check and handle fcntl(F_GETFD) failure (Tomás Andróil) b7288de subprocess: Proper implementation of wait() on Windows (Haowen Liu) 7423214 subprocess: Do not escape double quotes for command line arguments on Windows (Hennadii Stepanov) bb9ffea subprocess: Explicitly define move constructor of Streams class (Shunsuke Shimizu) 174bd43 subprocess: Avoid leaking POSIX name aliases beyond `subprocess.h` (Hennadii Stepanov) 7997b76 subprocess: Fix cross-compiling with mingw toolchain (Hennadii Stepanov) 6476304 subprocess: Get Windows return code in wait() (Haowen Liu) d3f511b subprocess: Fix string_arg when used with rref (Haowen Liu) 2fd3f2f subprocess: Fix memory leaks (Haoran Peng) Pull request description: Most of these changes were developed during work on bitcoin#29868 and bitcoin#32342 and have since been upstreamed. As they are now merged, this PR backports them to our `src/util/subprocess.h` header. Required for bitcoin#29868. A list of the backported PRs: - arun11299/cpp-subprocess#106 - arun11299/cpp-subprocess#110 - arun11299/cpp-subprocess#109 - arun11299/cpp-subprocess#99 - arun11299/cpp-subprocess#112 - arun11299/cpp-subprocess#107 - arun11299/cpp-subprocess#113 - arun11299/cpp-subprocess#116 - arun11299/cpp-subprocess#117 The following PRs were skipped for backporting: - arun11299/cpp-subprocess#108 because we are not planning to support this feature. - arun11299/cpp-subprocess#101 because that change has been already landed in bitcoin#29849. ACKs for top commit: theStack: Light ACK cd95c9d laanwj: Code review re-ACK cd95c9d Tree-SHA512: f9b60b932957d2e1cad1d87f2ad8bb68c97136e9735eb78547018a42cc50c4652750367f29462eadb0512c27db1dd8a7d4b17a2f0aeab62b3dbf86db5f51a61c
2 parents fa2c548 + cd95c9d commit 53ccb75

File tree

1 file changed

+82
-46
lines changed

1 file changed

+82
-46
lines changed

src/util/subprocess.h

+82-46
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,9 @@ Documentation for C++ subprocessing library.
6565

6666
extern "C" {
6767
#ifdef __USING_WINDOWS__
68-
#include <Windows.h>
68+
#include <windows.h>
6969
#include <io.h>
7070
#include <cwchar>
71-
72-
#define close _close
73-
#define open _open
74-
#define fileno _fileno
7571
#else
7672
#include <sys/wait.h>
7773
#include <unistd.h>
@@ -81,6 +77,22 @@ extern "C" {
8177
#include <sys/types.h>
8278
}
8379

80+
// The Microsoft C++ compiler issues deprecation warnings
81+
// for the standard POSIX function names.
82+
// Its preferred implementations have a leading underscore.
83+
// See: https://learn.microsoft.com/en-us/cpp/c-runtime-library/compatibility.
84+
#if (defined _MSC_VER)
85+
#define subprocess_close _close
86+
#define subprocess_fileno _fileno
87+
#define subprocess_open _open
88+
#define subprocess_write _write
89+
#else
90+
#define subprocess_close close
91+
#define subprocess_fileno fileno
92+
#define subprocess_open open
93+
#define subprocess_write write
94+
#endif
95+
8496
/*!
8597
* Getting started with reading this source code.
8698
* The source is mainly divided into four parts:
@@ -159,6 +171,7 @@ class OSError: public std::runtime_error
159171
//--------------------------------------------------------------------
160172
namespace util
161173
{
174+
#ifdef __USING_WINDOWS__
162175
inline void quote_argument(const std::wstring &argument, std::wstring &command_line,
163176
bool force)
164177
{
@@ -169,7 +182,7 @@ namespace util
169182
//
170183

171184
if (force == false && argument.empty() == false &&
172-
argument.find_first_of(L" \t\n\v\"") == argument.npos) {
185+
argument.find_first_of(L" \t\n\v") == argument.npos) {
173186
command_line.append(argument);
174187
}
175188
else {
@@ -219,7 +232,6 @@ namespace util
219232
}
220233
}
221234

222-
#ifdef __USING_WINDOWS__
223235
inline std::string get_last_error(DWORD errorMessageID)
224236
{
225237
if (errorMessageID == 0)
@@ -264,7 +276,7 @@ namespace util
264276

265277
FILE *fp = _fdopen(os_fhandle, mode);
266278
if (fp == 0) {
267-
_close(os_fhandle);
279+
subprocess_close(os_fhandle);
268280
throw OSError("_fdopen", 0);
269281
}
270282

@@ -334,10 +346,14 @@ namespace util
334346
void set_clo_on_exec(int fd, bool set = true)
335347
{
336348
int flags = fcntl(fd, F_GETFD, 0);
349+
if (flags == -1) {
350+
throw OSError("fcntl F_GETFD failed", errno);
351+
}
337352
if (set) flags |= FD_CLOEXEC;
338353
else flags &= ~FD_CLOEXEC;
339-
//TODO: should check for errors
340-
fcntl(fd, F_SETFD, flags);
354+
if (fcntl(fd, F_SETFD, flags) == -1) {
355+
throw OSError("fcntl F_SETFD failed", errno);
356+
}
341357
}
342358

343359

@@ -383,7 +399,7 @@ namespace util
383399
{
384400
size_t nwritten = 0;
385401
while (nwritten < length) {
386-
int written = write(fd, buf + nwritten, length - nwritten);
402+
int written = subprocess_write(fd, buf + nwritten, length - nwritten);
387403
if (written == -1) return -1;
388404
nwritten += written;
389405
}
@@ -411,7 +427,7 @@ namespace util
411427
#ifdef __USING_WINDOWS__
412428
return (int)fread(buf, 1, read_upto, fp);
413429
#else
414-
int fd = fileno(fp);
430+
int fd = subprocess_fileno(fp);
415431
int rbytes = 0;
416432
int eintr_cnter = 0;
417433

@@ -527,7 +543,7 @@ struct string_arg
527543
{
528544
string_arg(const char* arg): arg_value(arg) {}
529545
string_arg(std::string&& arg): arg_value(std::move(arg)) {}
530-
string_arg(std::string arg): arg_value(std::move(arg)) {}
546+
string_arg(const std::string& arg): arg_value(arg) {}
531547
std::string arg_value;
532548
};
533549

@@ -573,10 +589,10 @@ struct input
573589
explicit input(int fd): rd_ch_(fd) {}
574590

575591
// FILE pointer.
576-
explicit input (FILE* fp):input(fileno(fp)) { assert(fp); }
592+
explicit input (FILE* fp):input(subprocess_fileno(fp)) { assert(fp); }
577593

578594
explicit input(const char* filename) {
579-
int fd = open(filename, O_RDONLY);
595+
int fd = subprocess_open(filename, O_RDONLY);
580596
if (fd == -1) throw OSError("File not found: ", errno);
581597
rd_ch_ = fd;
582598
}
@@ -606,10 +622,10 @@ struct output
606622
{
607623
explicit output(int fd): wr_ch_(fd) {}
608624

609-
explicit output (FILE* fp):output(fileno(fp)) { assert(fp); }
625+
explicit output (FILE* fp):output(subprocess_fileno(fp)) { assert(fp); }
610626

611627
explicit output(const char* filename) {
612-
int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
628+
int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
613629
if (fd == -1) throw OSError("File not found: ", errno);
614630
wr_ch_ = fd;
615631
}
@@ -637,10 +653,10 @@ struct error
637653
{
638654
explicit error(int fd): wr_ch_(fd) {}
639655

640-
explicit error(FILE* fp):error(fileno(fp)) { assert(fp); }
656+
explicit error(FILE* fp):error(subprocess_fileno(fp)) { assert(fp); }
641657

642658
explicit error(const char* filename) {
643-
int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
659+
int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
644660
if (fd == -1) throw OSError("File not found: ", errno);
645661
wr_ch_ = fd;
646662
}
@@ -758,7 +774,10 @@ class Communication
758774
public:
759775
Communication(Streams* stream): stream_(stream)
760776
{}
761-
void operator=(const Communication&) = delete;
777+
Communication(const Communication&) = delete;
778+
Communication& operator=(const Communication&) = delete;
779+
Communication(Communication&&) = default;
780+
Communication& operator=(Communication&&) = default;
762781
public:
763782
int send(const char* msg, size_t length);
764783
int send(const std::vector<char>& msg);
@@ -795,36 +814,39 @@ class Streams
795814
{
796815
public:
797816
Streams():comm_(this) {}
798-
void operator=(const Streams&) = delete;
817+
Streams(const Streams&) = delete;
818+
Streams& operator=(const Streams&) = delete;
819+
Streams(Streams&&) = default;
820+
Streams& operator=(Streams&&) = default;
799821

800822
public:
801823
void setup_comm_channels();
802824

803825
void cleanup_fds()
804826
{
805827
if (write_to_child_ != -1 && read_from_parent_ != -1) {
806-
close(write_to_child_);
828+
subprocess_close(write_to_child_);
807829
}
808830
if (write_to_parent_ != -1 && read_from_child_ != -1) {
809-
close(read_from_child_);
831+
subprocess_close(read_from_child_);
810832
}
811833
if (err_write_ != -1 && err_read_ != -1) {
812-
close(err_read_);
834+
subprocess_close(err_read_);
813835
}
814836
}
815837

816838
void close_parent_fds()
817839
{
818-
if (write_to_child_ != -1) close(write_to_child_);
819-
if (read_from_child_ != -1) close(read_from_child_);
820-
if (err_read_ != -1) close(err_read_);
840+
if (write_to_child_ != -1) subprocess_close(write_to_child_);
841+
if (read_from_child_ != -1) subprocess_close(read_from_child_);
842+
if (err_read_ != -1) subprocess_close(err_read_);
821843
}
822844

823845
void close_child_fds()
824846
{
825-
if (write_to_parent_ != -1) close(write_to_parent_);
826-
if (read_from_parent_ != -1) close(read_from_parent_);
827-
if (err_write_ != -1) close(err_write_);
847+
if (write_to_parent_ != -1) subprocess_close(write_to_parent_);
848+
if (read_from_parent_ != -1) subprocess_close(read_from_parent_);
849+
if (err_write_ != -1) subprocess_close(err_write_);
828850
}
829851

830852
FILE* input() { return input_.get(); }
@@ -1043,7 +1065,19 @@ inline int Popen::wait() noexcept(false)
10431065
#ifdef __USING_WINDOWS__
10441066
int ret = WaitForSingleObject(process_handle_, INFINITE);
10451067

1046-
return 0;
1068+
// WaitForSingleObject with INFINITE should only return when process has signaled
1069+
if (ret != WAIT_OBJECT_0) {
1070+
throw OSError("Unexpected return code from WaitForSingleObject", 0);
1071+
}
1072+
1073+
DWORD dretcode_;
1074+
1075+
if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
1076+
throw OSError("Failed during call to GetExitCodeProcess", 0);
1077+
1078+
CloseHandle(process_handle_);
1079+
1080+
return (int)dretcode_;
10471081
#else
10481082
int ret, status;
10491083
std::tie(ret, status) = util::wait_for_child_exit(child_pid_);
@@ -1156,8 +1190,8 @@ inline void Popen::execute_process() noexcept(false)
11561190
child_pid_ = fork();
11571191

11581192
if (child_pid_ < 0) {
1159-
close(err_rd_pipe);
1160-
close(err_wr_pipe);
1193+
subprocess_close(err_rd_pipe);
1194+
subprocess_close(err_wr_pipe);
11611195
throw OSError("fork failed", errno);
11621196
}
11631197

@@ -1167,25 +1201,27 @@ inline void Popen::execute_process() noexcept(false)
11671201
stream_.close_parent_fds();
11681202

11691203
//Close the read end of the error pipe
1170-
close(err_rd_pipe);
1204+
subprocess_close(err_rd_pipe);
11711205

11721206
detail::Child chld(this, err_wr_pipe);
11731207
chld.execute_child();
11741208
}
11751209
else
11761210
{
1177-
close (err_wr_pipe);// close child side of pipe, else get stuck in read below
1211+
subprocess_close(err_wr_pipe);// close child side of pipe, else get stuck in read below
11781212

11791213
stream_.close_child_fds();
11801214

11811215
try {
11821216
char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
11831217

1184-
int read_bytes = util::read_atmost_n(
1185-
fdopen(err_rd_pipe, "r"),
1186-
err_buf,
1187-
SP_MAX_ERR_BUF_SIZ);
1188-
close(err_rd_pipe);
1218+
FILE* err_fp = fdopen(err_rd_pipe, "r");
1219+
if (!err_fp) {
1220+
subprocess_close(err_rd_pipe);
1221+
throw OSError("fdopen failed", errno);
1222+
}
1223+
int read_bytes = util::read_atmost_n(err_fp, err_buf, SP_MAX_ERR_BUF_SIZ);
1224+
fclose(err_fp);
11891225

11901226
if (read_bytes || strlen(err_buf)) {
11911227
// Call waitpid to reap the child process
@@ -1271,13 +1307,13 @@ namespace detail {
12711307

12721308
// Close the duped descriptors
12731309
if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2)
1274-
close(stream.read_from_parent_);
1310+
subprocess_close(stream.read_from_parent_);
12751311

12761312
if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2)
1277-
close(stream.write_to_parent_);
1313+
subprocess_close(stream.write_to_parent_);
12781314

12791315
if (stream.err_write_ != -1 && stream.err_write_ > 2)
1280-
close(stream.err_write_);
1316+
subprocess_close(stream.err_write_);
12811317

12821318
// Replace the current image with the executable
12831319
sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
@@ -1304,15 +1340,15 @@ namespace detail {
13041340
#ifdef __USING_WINDOWS__
13051341
util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
13061342
this->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w"));
1307-
this->write_to_child_ = _fileno(this->input());
1343+
this->write_to_child_ = subprocess_fileno(this->input());
13081344

13091345
util::configure_pipe(&this->g_hChildStd_OUT_Rd, &this->g_hChildStd_OUT_Wr, &this->g_hChildStd_OUT_Rd);
13101346
this->output(util::file_from_handle(this->g_hChildStd_OUT_Rd, "r"));
1311-
this->read_from_child_ = _fileno(this->output());
1347+
this->read_from_child_ = subprocess_fileno(this->output());
13121348

13131349
util::configure_pipe(&this->g_hChildStd_ERR_Rd, &this->g_hChildStd_ERR_Wr, &this->g_hChildStd_ERR_Rd);
13141350
this->error(util::file_from_handle(this->g_hChildStd_ERR_Rd, "r"));
1315-
this->err_read_ = _fileno(this->error());
1351+
this->err_read_ = subprocess_fileno(this->error());
13161352
#else
13171353

13181354
if (write_to_child_ != -1) input(fdopen(write_to_child_, "wb"));

0 commit comments

Comments
 (0)