Skip to content
Bitcoin Roots

v29.4-roots.3 · Commit 2: [PATCH 02/30] runtime: preserve shared operator controls

src/util/subprocess.h

Before: src/util/subprocess.h · After: src/util/subprocess.h

File metadata
index 3449fa3b1bacce60422267110b27e54b13b19458..7cabd1baf27c5366aea0fed3a6b6bbb4ded01475 100644

Download original patch · Release notes

Diff rows 1–400 of 478. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.

src/util/subprocess.h CHANGED
@@ -36,6 +36,8 @@ Documentation for C++ subprocessing library.
36
  #ifndef BITCOIN_UTIL_SUBPROCESS_H
37
  #define BITCOIN_UTIL_SUBPROCESS_H
38
 
 
 
39
  #include <util/syserror.h>
40
 
41
  #include <algorithm>
@@ -65,13 +67,9 @@ Documentation for C++ subprocessing library.
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>
@@ -81,6 +79,22 @@ extern "C" {
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:
@@ -159,6 +173,7 @@ public:
159
  //--------------------------------------------------------------------
160
  namespace util
161
  {
 
162
  inline void quote_argument(const std::wstring &argument, std::wstring &command_line,
163
  bool force)
164
  {
@@ -169,7 +184,7 @@ namespace util
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 {
@@ -219,7 +234,6 @@ namespace util
219
  }
220
  }
221
 
222
- #ifdef __USING_WINDOWS__
223
  inline std::string get_last_error(DWORD errorMessageID)
224
  {
225
  if (errorMessageID == 0)
@@ -264,7 +278,7 @@ namespace util
264
 
265
  FILE *fp = _fdopen(os_fhandle, mode);
266
  if (fp == 0) {
267
- _close(os_fhandle);
268
  throw OSError("_fdopen", 0);
269
  }
270
 
@@ -334,10 +348,14 @@ namespace util
334
  void set_clo_on_exec(int fd, bool set = true)
335
  {
336
  int flags = fcntl(fd, F_GETFD, 0);
 
 
 
337
  if (set) flags |= FD_CLOEXEC;
338
  else flags &= ~FD_CLOEXEC;
339
- //TODO: should check for errors
340
- fcntl(fd, F_SETFD, flags);
 
341
  }
342
 
343
 
@@ -383,7 +401,7 @@ namespace util
383
  {
384
  size_t nwritten = 0;
385
  while (nwritten < length) {
386
- int written = write(fd, buf + nwritten, length - nwritten);
387
  if (written == -1) return -1;
388
  nwritten += written;
389
  }
@@ -411,7 +429,7 @@ namespace util
411
  #ifdef __USING_WINDOWS__
412
  return (int)fread(buf, 1, read_upto, fp);
413
  #else
414
- int fd = fileno(fp);
415
  int rbytes = 0;
416
  int eintr_cnter = 0;
417
 
@@ -520,6 +538,20 @@ namespace util
520
  * -------------------------------
521
  */
522
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
  /*!
524
  * Base class for all arguments involving string value.
525
  */
@@ -527,7 +559,7 @@ 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
 
@@ -573,10 +605,10 @@ struct input
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
  }
@@ -606,10 +638,10 @@ 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
  }
@@ -637,10 +669,10 @@ 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
  }
@@ -717,6 +749,7 @@ struct ArgumentDeducer
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;
@@ -758,7 +791,10 @@ class Communication
758
  public:
759
  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);
@@ -795,7 +831,10 @@ class Streams
795
  {
796
  public:
797
  Streams():comm_(this) {}
798
- void operator=(const Streams&) = delete;
 
 
 
799
 
800
  public:
801
  void setup_comm_channels();
@@ -803,28 +842,28 @@ public:
803
  void cleanup_fds()
804
  {
805
  if (write_to_child_ != -1 && read_from_parent_ != -1) {
806
- close(write_to_child_);
807
  }
808
  if (write_to_parent_ != -1 && read_from_child_ != -1) {
809
- close(read_from_child_);
810
  }
811
  if (err_write_ != -1 && err_read_ != -1) {
812
- close(err_read_);
813
  }
814
  }
815
 
816
  void close_parent_fds()
817
  {
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_);
821
  }
822
 
823
  void close_child_fds()
824
  {
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_);
828
  }
829
 
830
  FILE* input() { return input_.get(); }
@@ -902,6 +941,8 @@ private:
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.
@@ -952,6 +993,12 @@ public:
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); }
@@ -1004,6 +1051,8 @@ private:
1004
  std::future<void> cleanup_future_;
1005
  #endif
1006
 
 
 
1007
  std::string exe_name_;
1008
 
1009
  // Command in string format
@@ -1012,6 +1061,7 @@ private:
1012
  std::vector<std::string> vargs_;
1013
  std::vector<char*> cargv_;
1014
 
 
1015
  // Pid of the child process
1016
  int child_pid_ = -1;
1017
 
@@ -1043,7 +1093,19 @@ inline int Popen::wait() noexcept(false)
1043
  #ifdef __USING_WINDOWS__
1044
  int ret = WaitForSingleObject(process_handle_, INFINITE);
1045
 
1046
- return 0;
 
 
 
 
 
 
 
 
 
 
 
 
1047
  #else
1048
  int ret, status;
1049
  std::tie(ret, status) = util::wait_for_child_exit(child_pid_);
@@ -1059,6 +1121,68 @@ inline int Popen::wait() noexcept(false)
1059
  #endif
1060
  }
1061
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1062
  inline void Popen::execute_process() noexcept(false)
1063
  {
1064
  #ifdef __USING_WINDOWS__
@@ -1071,11 +1195,16 @@ inline void Popen::execute_process() noexcept(false)
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
@@ -1156,36 +1285,40 @@ inline void Popen::execute_process() noexcept(false)
1156
  child_pid_ = fork();
1157
 
1158
  if (child_pid_ < 0) {
1159
- close(err_rd_pipe);
1160
- close(err_wr_pipe);
1161
  throw OSError("fork failed", errno);
1162
  }
1163
 
 
 
1164
  if (child_pid_ == 0)
1165
  {
1166
  // Close descriptors belonging to parent
1167
  stream_.close_parent_fds();
1168
 
1169
  //Close the read end of the error pipe
1170
- close(err_rd_pipe);
1171
 
1172
  detail::Child chld(this, err_wr_pipe);
1173
  chld.execute_child();
1174
  }
 
36
  #ifndef BITCOIN_UTIL_SUBPROCESS_H
37
  #define BITCOIN_UTIL_SUBPROCESS_H
38
 
39
+ #include <util/fs.h>
40
+ #include <util/strencodings.h>
41
  #include <util/syserror.h>
42
 
43
  #include <algorithm>
 
67
 
68
  extern "C" {
69
  #ifdef __USING_WINDOWS__
70
+ #include <windows.h>
71
  #include <io.h>
72
  #include <cwchar>
 
 
 
 
73
  #else
74
  #include <sys/wait.h>
75
  #include <unistd.h>
 
79
  #include <sys/types.h>
80
  }
81
 
82
+ // The Microsoft C++ compiler issues deprecation warnings
83
+ // for the standard POSIX function names.
84
+ // Its preferred implementations have a leading underscore.
85
+ // See: https://learn.microsoft.com/en-us/cpp/c-runtime-library/compatibility.
86
+ #if (defined _MSC_VER)
87
+ #define subprocess_close _close
88
+ #define subprocess_fileno _fileno
89
+ #define subprocess_open _open
90
+ #define subprocess_write _write
91
+ #else
92
+ #define subprocess_close close
93
+ #define subprocess_fileno fileno
94
+ #define subprocess_open open
95
+ #define subprocess_write write
96
+ #endif
97
+
98
  /*!
99
  * Getting started with reading this source code.
100
  * The source is mainly divided into four parts:
 
173
  //--------------------------------------------------------------------
174
  namespace util
175
  {
176
+ #ifdef __USING_WINDOWS__
177
  inline void quote_argument(const std::wstring &argument, std::wstring &command_line,
178
  bool force)
179
  {
 
184
  //
185
 
186
  if (force == false && argument.empty() == false &&
187
+ argument.find_first_of(L" \t\n\v") == argument.npos) {
188
  command_line.append(argument);
189
  }
190
  else {
 
234
  }
235
  }
236
 
 
237
  inline std::string get_last_error(DWORD errorMessageID)
238
  {
239
  if (errorMessageID == 0)
 
278
 
279
  FILE *fp = _fdopen(os_fhandle, mode);
280
  if (fp == 0) {
281
+ subprocess_close(os_fhandle);
282
  throw OSError("_fdopen", 0);
283
  }
284
 
 
348
  void set_clo_on_exec(int fd, bool set = true)
349
  {
350
  int flags = fcntl(fd, F_GETFD, 0);
351
+ if (flags == -1) {
352
+ throw OSError("fcntl F_GETFD failed", errno);
353
+ }
354
  if (set) flags |= FD_CLOEXEC;
355
  else flags &= ~FD_CLOEXEC;
356
+ if (fcntl(fd, F_SETFD, flags) == -1) {
357
+ throw OSError("fcntl F_SETFD failed", errno);
358
+ }
359
  }
360
 
361
 
 
401
  {
402
  size_t nwritten = 0;
403
  while (nwritten < length) {
404
+ int written = subprocess_write(fd, buf + nwritten, length - nwritten);
405
  if (written == -1) return -1;
406
  nwritten += written;
407
  }
 
429
  #ifdef __USING_WINDOWS__
430
  return (int)fread(buf, 1, read_upto, fp);
431
  #else
432
+ int fd = subprocess_fileno(fp);
433
  int rbytes = 0;
434
  int eintr_cnter = 0;
435
 
 
538
  * -------------------------------
539
  */
540
 
541
+ /*!
542
+ * Option to close all file descriptors
543
+ * when the child process is spawned.
544
+ * The close fd list does not include
545
+ * input/output/error if they are explicitly
546
+ * set as part of the Popen arguments.
547
+ *
548
+ * Default value is false.
549
+ */
550
+ struct close_fds {
551
+ explicit close_fds(bool c): close_all(c) {}
552
+ bool close_all = false;
553
+ };
554
+
555
  /*!
556
  * Base class for all arguments involving string value.
557
  */
 
559
  {
560
  string_arg(const char* arg): arg_value(arg) {}
561
  string_arg(std::string&& arg): arg_value(std::move(arg)) {}
562
+ string_arg(const std::string& arg): arg_value(arg) {}
563
  std::string arg_value;
564
  };
565
 
 
605
  explicit input(int fd): rd_ch_(fd) {}
606
 
607
  // FILE pointer.
608
+ explicit input (FILE* fp):input(subprocess_fileno(fp)) { assert(fp); }
609
 
610
  explicit input(const char* filename) {
611
+ int fd = subprocess_open(filename, O_RDONLY);
612
  if (fd == -1) throw OSError("File not found: ", errno);
613
  rd_ch_ = fd;
614
  }
 
638
  {
639
  explicit output(int fd): wr_ch_(fd) {}
640
 
641
+ explicit output (FILE* fp):output(subprocess_fileno(fp)) { assert(fp); }
642
 
643
  explicit output(const char* filename) {
644
+ int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
645
  if (fd == -1) throw OSError("File not found: ", errno);
646
  wr_ch_ = fd;
647
  }
 
669
  {
670
  explicit error(int fd): wr_ch_(fd) {}
671
 
672
+ explicit error(FILE* fp):error(subprocess_fileno(fp)) { assert(fp); }
673
 
674
  explicit error(const char* filename) {
675
+ int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
676
  if (fd == -1) throw OSError("File not found: ", errno);
677
  wr_ch_ = fd;
678
  }
 
749
  void set_option(input&& inp);
750
  void set_option(output&& out);
751
  void set_option(error&& err);
752
+ void set_option(close_fds&& cfds);
753
 
754
  private:
755
  Popen* popen_ = nullptr;
 
791
  public:
792
  Communication(Streams* stream): stream_(stream)
793
  {}
794
+ Communication(const Communication&) = delete;
795
+ Communication& operator=(const Communication&) = delete;
796
+ Communication(Communication&&) = default;
797
+ Communication& operator=(Communication&&) = default;
798
  public:
799
  int send(const char* msg, size_t length);
800
  int send(const std::vector<char>& msg);
 
831
  {
832
  public:
833
  Streams():comm_(this) {}
834
+ Streams(const Streams&) = delete;
835
+ Streams& operator=(const Streams&) = delete;
836
+ Streams(Streams&&) = default;
837
+ Streams& operator=(Streams&&) = default;
838
 
839
  public:
840
  void setup_comm_channels();
 
842
  void cleanup_fds()
843
  {
844
  if (write_to_child_ != -1 && read_from_parent_ != -1) {
845
+ subprocess_close(write_to_child_);
846
  }
847
  if (write_to_parent_ != -1 && read_from_child_ != -1) {
848
+ subprocess_close(read_from_child_);
849
  }
850
  if (err_write_ != -1 && err_read_ != -1) {
851
+ subprocess_close(err_read_);
852
  }
853
  }
854
 
855
  void close_parent_fds()
856
  {
857
+ if (write_to_child_ != -1) subprocess_close(write_to_child_);
858
+ if (read_from_child_ != -1) subprocess_close(read_from_child_);
859
+ if (err_read_ != -1) subprocess_close(err_read_);
860
  }
861
 
862
  void close_child_fds()
863
  {
864
+ if (write_to_parent_ != -1) subprocess_close(write_to_parent_);
865
+ if (read_from_parent_ != -1) subprocess_close(read_from_parent_);
866
+ if (err_write_ != -1) subprocess_close(err_write_);
867
  }
868
 
869
  FILE* input() { return input_.get(); }
 
941
  * Command provided in a single string.
942
  * wait() - Wait for the child to exit.
943
  * retcode() - The return code of the exited child.
944
+ * poll() - Check the status of the running child.
945
+ * kill(sig_num) - Kill the child. SIGTERM used by default.
946
  * send(...) - Send input to the input channel of the child.
947
  * communicate(...) - Get the output/error from the child and close the channels
948
  * from the parent side.
 
993
 
994
  int wait() noexcept(false);
995
 
996
+ int poll() noexcept(false);
997
+
998
+ // Does not fail, Caller is expected to recheck the
999
+ // status with a call to poll()
1000
+ void kill(int sig_num = 9);
1001
+
1002
  void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
1003
 
1004
  void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
 
1051
  std::future<void> cleanup_future_;
1052
  #endif
1053
 
1054
+ bool close_fds_ = false;
1055
+
1056
  std::string exe_name_;
1057
 
1058
  // Command in string format
 
1061
  std::vector<std::string> vargs_;
1062
  std::vector<char*> cargv_;
1063
 
1064
+ bool child_created_ = false;
1065
  // Pid of the child process
1066
  int child_pid_ = -1;
1067
 
 
1093
  #ifdef __USING_WINDOWS__
1094
  int ret = WaitForSingleObject(process_handle_, INFINITE);
1095
 
1096
+ // WaitForSingleObject with INFINITE should only return when process has signaled
1097
+ if (ret != WAIT_OBJECT_0) {
1098
+ throw OSError("Unexpected return code from WaitForSingleObject", 0);
1099
+ }
1100
+
1101
+ DWORD dretcode_;
1102
+
1103
+ if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
1104
+ throw OSError("Failed during call to GetExitCodeProcess", 0);
1105
+
1106
+ CloseHandle(process_handle_);
1107
+
1108
+ return (int)dretcode_;
1109
  #else
1110
  int ret, status;
1111
  std::tie(ret, status) = util::wait_for_child_exit(child_pid_);
 
1121
  #endif
1122
  }
1123
 
1124
+ inline int Popen::poll() noexcept(false)
1125
+ {
1126
+ #ifdef __USING_WINDOWS__
1127
+ int ret = WaitForSingleObject(process_handle_, 0);
1128
+ if (ret != WAIT_OBJECT_0) return -1;
1129
+
1130
+ DWORD dretcode_;
1131
+ if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
1132
+ throw OSError("GetExitCodeProcess", 0);
1133
+
1134
+ retcode_ = (int)dretcode_;
1135
+ CloseHandle(process_handle_);
1136
+
1137
+ return retcode_;
1138
+ #else
1139
+ if (!child_created_) return -1; // TODO: ??
1140
+
1141
+ int status;
1142
+
1143
+ // Returns zero if child is still running
1144
+ int ret = waitpid(child_pid_, &status, WNOHANG);
1145
+ if (ret == 0) return -1;
1146
+
1147
+ if (ret == child_pid_) {
1148
+ if (WIFSIGNALED(status)) {
1149
+ retcode_ = WTERMSIG(status);
1150
+ } else if (WIFEXITED(status)) {
1151
+ retcode_ = WEXITSTATUS(status);
1152
+ } else {
1153
+ retcode_ = 255;
1154
+ }
1155
+ return retcode_;
1156
+ }
1157
+
1158
+ if (ret == -1) {
1159
+ // From subprocess.py
1160
+ // This happens if SIGCHLD is set to be ignored
1161
+ // or waiting for child process has otherwise been disabled
1162
+ // for our process. This child is dead, we cannot get the
1163
+ // status.
1164
+ if (errno == ECHILD) retcode_ = 0;
1165
+ else throw OSError("waitpid failed", errno);
1166
+ } else {
1167
+ retcode_ = ret;
1168
+ }
1169
+
1170
+ return retcode_;
1171
+ #endif
1172
+ }
1173
+
1174
+ inline void Popen::kill(int sig_num)
1175
+ {
1176
+ #ifdef __USING_WINDOWS__
1177
+ if (!TerminateProcess(this->process_handle_, (UINT)sig_num)) {
1178
+ throw OSError("TerminateProcess", 0);
1179
+ }
1180
+ #else
1181
+ ::kill(child_pid_, sig_num);
1182
+ #endif
1183
+ }
1184
+
1185
+
1186
  inline void Popen::execute_process() noexcept(false)
1187
  {
1188
  #ifdef __USING_WINDOWS__
 
1195
  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
1196
  std::wstring argument;
1197
  std::wstring command_line;
1198
+ bool first_arg = true;
1199
 
1200
  for (auto arg : this->vargs_) {
1201
+ if (!first_arg) {
1202
+ command_line += L" ";
1203
+ } else {
1204
+ first_arg = false;
1205
+ }
1206
  argument = converter.from_bytes(arg);
1207
  util::quote_argument(argument, command_line, false);
 
1208
  }
1209
 
1210
  // CreateProcessW can modify szCmdLine so we allocate needed memory
 
1285
  child_pid_ = fork();
1286
 
1287
  if (child_pid_ < 0) {
1288
+ subprocess_close(err_rd_pipe);
1289
+ subprocess_close(err_wr_pipe);
1290
  throw OSError("fork failed", errno);
1291
  }
1292
 
1293
+ child_created_ = true;
1294
+
1295
  if (child_pid_ == 0)
1296
  {
1297
  // Close descriptors belonging to parent
1298
  stream_.close_parent_fds();
1299
 
1300
  //Close the read end of the error pipe
1301
+ subprocess_close(err_rd_pipe);
1302
 
1303
  detail::Child chld(this, err_wr_pipe);
1304
  chld.execute_child();
1305
  }