Skip to content
Bitcoin Roots

v29.4-roots.2 · Commit 2: [PATCH 02/21] 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. The two line-number columns refer to before and after.

src/util/subprocess.h CHANGED
@@ -36,6 +36,8 @@ Documentation for C++ subprocessing library.
36
36
  #ifndef BITCOIN_UTIL_SUBPROCESS_H
37
37
  #define BITCOIN_UTIL_SUBPROCESS_H
38
38
 
39
+ #include <util/fs.h>
40
+ #include <util/strencodings.h>
39
41
  #include <util/syserror.h>
40
42
 
41
43
  #include <algorithm>
@@ -65,13 +67,9 @@ Documentation for C++ subprocessing library.
65
67
 
66
68
  extern "C" {
67
69
  #ifdef __USING_WINDOWS__
68
- #include <Windows.h>
70
+ #include <windows.h>
69
71
  #include <io.h>
70
72
  #include <cwchar>
71
-
72
- #define close _close
73
- #define open _open
74
- #define fileno _fileno
75
73
  #else
76
74
  #include <sys/wait.h>
77
75
  #include <unistd.h>
@@ -81,6 +79,22 @@ extern "C" {
81
79
  #include <sys/types.h>
82
80
  }
83
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
+
84
98
  /*!
85
99
  * Getting started with reading this source code.
86
100
  * The source is mainly divided into four parts:
@@ -159,6 +173,7 @@ public:
159
173
  //--------------------------------------------------------------------
160
174
  namespace util
161
175
  {
176
+ #ifdef __USING_WINDOWS__
162
177
  inline void quote_argument(const std::wstring &argument, std::wstring &command_line,
163
178
  bool force)
164
179
  {
@@ -169,7 +184,7 @@ namespace util
169
184
  //
170
185
 
171
186
  if (force == false && argument.empty() == false &&
172
- argument.find_first_of(L" \t\n\v\"") == argument.npos) {
187
+ argument.find_first_of(L" \t\n\v") == argument.npos) {
173
188
  command_line.append(argument);
174
189
  }
175
190
  else {
@@ -219,7 +234,6 @@ namespace util
219
234
  }
220
235
  }
221
236
 
222
- #ifdef __USING_WINDOWS__
223
237
  inline std::string get_last_error(DWORD errorMessageID)
224
238
  {
225
239
  if (errorMessageID == 0)
@@ -264,7 +278,7 @@ namespace util
264
278
 
265
279
  FILE *fp = _fdopen(os_fhandle, mode);
266
280
  if (fp == 0) {
267
- _close(os_fhandle);
281
+ subprocess_close(os_fhandle);
268
282
  throw OSError("_fdopen", 0);
269
283
  }
270
284
 
@@ -334,10 +348,14 @@ namespace util
334
348
  void set_clo_on_exec(int fd, bool set = true)
335
349
  {
336
350
  int flags = fcntl(fd, F_GETFD, 0);
351
+ if (flags == -1) {
352
+ throw OSError("fcntl F_GETFD failed", errno);
353
+ }
337
354
  if (set) flags |= FD_CLOEXEC;
338
355
  else flags &= ~FD_CLOEXEC;
339
- //TODO: should check for errors
340
- fcntl(fd, F_SETFD, flags);
356
+ if (fcntl(fd, F_SETFD, flags) == -1) {
357
+ throw OSError("fcntl F_SETFD failed", errno);
358
+ }
341
359
  }
342
360
 
343
361
 
@@ -383,7 +401,7 @@ namespace util
383
401
  {
384
402
  size_t nwritten = 0;
385
403
  while (nwritten < length) {
386
- int written = write(fd, buf + nwritten, length - nwritten);
404
+ int written = subprocess_write(fd, buf + nwritten, length - nwritten);
387
405
  if (written == -1) return -1;
388
406
  nwritten += written;
389
407
  }
@@ -411,7 +429,7 @@ namespace util
411
429
  #ifdef __USING_WINDOWS__
412
430
  return (int)fread(buf, 1, read_upto, fp);
413
431
  #else
414
- int fd = fileno(fp);
432
+ int fd = subprocess_fileno(fp);
415
433
  int rbytes = 0;
416
434
  int eintr_cnter = 0;
417
435
 
@@ -520,6 +538,20 @@ namespace util
520
538
  * -------------------------------
521
539
  */
522
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
+
523
555
  /*!
524
556
  * Base class for all arguments involving string value.
525
557
  */
@@ -527,7 +559,7 @@ struct string_arg
527
559
  {
528
560
  string_arg(const char* arg): arg_value(arg) {}
529
561
  string_arg(std::string&& arg): arg_value(std::move(arg)) {}
530
- string_arg(std::string arg): arg_value(std::move(arg)) {}
562
+ string_arg(const std::string& arg): arg_value(arg) {}
531
563
  std::string arg_value;
532
564
  };
533
565
 
@@ -573,10 +605,10 @@ struct input
573
605
  explicit input(int fd): rd_ch_(fd) {}
574
606
 
575
607
  // FILE pointer.
576
- explicit input (FILE* fp):input(fileno(fp)) { assert(fp); }
608
+ explicit input (FILE* fp):input(subprocess_fileno(fp)) { assert(fp); }
577
609
 
578
610
  explicit input(const char* filename) {
579
- int fd = open(filename, O_RDONLY);
611
+ int fd = subprocess_open(filename, O_RDONLY);
580
612
  if (fd == -1) throw OSError("File not found: ", errno);
581
613
  rd_ch_ = fd;
582
614
  }
@@ -606,10 +638,10 @@ struct output
606
638
  {
607
639
  explicit output(int fd): wr_ch_(fd) {}
608
640
 
609
- explicit output (FILE* fp):output(fileno(fp)) { assert(fp); }
641
+ explicit output (FILE* fp):output(subprocess_fileno(fp)) { assert(fp); }
610
642
 
611
643
  explicit output(const char* filename) {
612
- int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
644
+ int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
613
645
  if (fd == -1) throw OSError("File not found: ", errno);
614
646
  wr_ch_ = fd;
615
647
  }
@@ -637,10 +669,10 @@ struct error
637
669
  {
638
670
  explicit error(int fd): wr_ch_(fd) {}
639
671
 
640
- explicit error(FILE* fp):error(fileno(fp)) { assert(fp); }
672
+ explicit error(FILE* fp):error(subprocess_fileno(fp)) { assert(fp); }
641
673
 
642
674
  explicit error(const char* filename) {
643
- int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
675
+ int fd = subprocess_open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
644
676
  if (fd == -1) throw OSError("File not found: ", errno);
645
677
  wr_ch_ = fd;
646
678
  }
@@ -717,6 +749,7 @@ struct ArgumentDeducer
717
749
  void set_option(input&& inp);
718
750
  void set_option(output&& out);
719
751
  void set_option(error&& err);
752
+ void set_option(close_fds&& cfds);
720
753
 
721
754
  private:
722
755
  Popen* popen_ = nullptr;
@@ -758,7 +791,10 @@ class Communication
758
791
  public:
759
792
  Communication(Streams* stream): stream_(stream)
760
793
  {}
761
- void operator=(const Communication&) = delete;
794
+ Communication(const Communication&) = delete;
795
+ Communication& operator=(const Communication&) = delete;
796
+ Communication(Communication&&) = default;
797
+ Communication& operator=(Communication&&) = default;
762
798
  public:
763
799
  int send(const char* msg, size_t length);
764
800
  int send(const std::vector<char>& msg);
@@ -795,7 +831,10 @@ class Streams
795
831
  {
796
832
  public:
797
833
  Streams():comm_(this) {}
798
- void operator=(const Streams&) = delete;
834
+ Streams(const Streams&) = delete;
835
+ Streams& operator=(const Streams&) = delete;
836
+ Streams(Streams&&) = default;
837
+ Streams& operator=(Streams&&) = default;
799
838
 
800
839
  public:
801
840
  void setup_comm_channels();
@@ -803,28 +842,28 @@ public:
803
842
  void cleanup_fds()
804
843
  {
805
844
  if (write_to_child_ != -1 && read_from_parent_ != -1) {
806
- close(write_to_child_);
845
+ subprocess_close(write_to_child_);
807
846
  }
808
847
  if (write_to_parent_ != -1 && read_from_child_ != -1) {
809
- close(read_from_child_);
848
+ subprocess_close(read_from_child_);
810
849
  }
811
850
  if (err_write_ != -1 && err_read_ != -1) {
812
- close(err_read_);
851
+ subprocess_close(err_read_);
813
852
  }
814
853
  }
815
854
 
816
855
  void close_parent_fds()
817
856
  {
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_);
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_);
821
860
  }
822
861
 
823
862
  void close_child_fds()
824
863
  {
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_);
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_);
828
867
  }
829
868
 
830
869
  FILE* input() { return input_.get(); }
@@ -902,6 +941,8 @@ private:
902
941
  * Command provided in a single string.
903
942
  * wait() - Wait for the child to exit.
904
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.
905
946
  * send(...) - Send input to the input channel of the child.
906
947
  * communicate(...) - Get the output/error from the child and close the channels
907
948
  * from the parent side.
@@ -952,6 +993,12 @@ public:
952
993
 
953
994
  int wait() noexcept(false);
954
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
+
955
1002
  void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
956
1003
 
957
1004
  void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
@@ -1004,6 +1051,8 @@ private:
1004
1051
  std::future<void> cleanup_future_;
1005
1052
  #endif
1006
1053
 
1054
+ bool close_fds_ = false;
1055
+
1007
1056
  std::string exe_name_;
1008
1057
 
1009
1058
  // Command in string format
@@ -1012,6 +1061,7 @@ private:
1012
1061
  std::vector<std::string> vargs_;
1013
1062
  std::vector<char*> cargv_;
1014
1063
 
1064
+ bool child_created_ = false;
1015
1065
  // Pid of the child process
1016
1066
  int child_pid_ = -1;
1017
1067
 
@@ -1043,7 +1093,19 @@ inline int Popen::wait() noexcept(false)
1043
1093
  #ifdef __USING_WINDOWS__
1044
1094
  int ret = WaitForSingleObject(process_handle_, INFINITE);
1045
1095
 
1046
- return 0;
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_;
1047
1109
  #else
1048
1110
  int ret, status;
1049
1111
  std::tie(ret, status) = util::wait_for_child_exit(child_pid_);
@@ -1059,6 +1121,68 @@ inline int Popen::wait() noexcept(false)
1059
1121
  #endif
1060
1122
  }
1061
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
+
1062
1186
  inline void Popen::execute_process() noexcept(false)
1063
1187
  {
1064
1188
  #ifdef __USING_WINDOWS__
@@ -1071,11 +1195,16 @@ inline void Popen::execute_process() noexcept(false)
1071
1195
  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
1072
1196
  std::wstring argument;
1073
1197
  std::wstring command_line;
1198
+ bool first_arg = true;
1074
1199
 
1075
1200
  for (auto arg : this->vargs_) {
1201
+ if (!first_arg) {
1202
+ command_line += L" ";
1203
+ } else {
1204
+ first_arg = false;
1205
+ }
1076
1206
  argument = converter.from_bytes(arg);
1077
1207
  util::quote_argument(argument, command_line, false);
1078
- command_line += L" ";
1079
1208
  }
1080
1209
 
1081
1210
  // CreateProcessW can modify szCmdLine so we allocate needed memory
@@ -1156,36 +1285,40 @@ inline void Popen::execute_process() noexcept(false)
1156
1285
  child_pid_ = fork();
1157
1286
 
1158
1287
  if (child_pid_ < 0) {
1159
- close(err_rd_pipe);
1160
- close(err_wr_pipe);
1288
+ subprocess_close(err_rd_pipe);
1289
+ subprocess_close(err_wr_pipe);
1161
1290
  throw OSError("fork failed", errno);
1162
1291
  }
1163
1292
 
1293
+ child_created_ = true;
1294
+
1164
1295
  if (child_pid_ == 0)
1165
1296
  {
1166
1297
  // Close descriptors belonging to parent
1167
1298
  stream_.close_parent_fds();
1168
1299
 
1169
1300
  //Close the read end of the error pipe
1170
- close(err_rd_pipe);
1301
+ subprocess_close(err_rd_pipe);
1171
1302
 
1172
1303
  detail::Child chld(this, err_wr_pipe);
1173
1304
  chld.execute_child();
1174
1305
  }