Skip to content
Bitcoin Roots

v30.3-roots.1 · Commit 2: [PATCH 02/68] feat(runtime): preserve Roots operator controls on Core 30.3

src/util/subprocess.h

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

File metadata
index 9abca911d827b4e5c124dff25f802dbf2acd8c76..7cabd1baf27c5366aea0fed3a6b6bbb4ded01475 100644

Download original patch · Release notes

Diff rows 1–171 of 171. 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>
@@ -536,6 +538,20 @@ namespace util
536
538
  * -------------------------------
537
539
  */
538
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
+
539
555
  /*!
540
556
  * Base class for all arguments involving string value.
541
557
  */
@@ -733,6 +749,7 @@ struct ArgumentDeducer
733
749
  void set_option(input&& inp);
734
750
  void set_option(output&& out);
735
751
  void set_option(error&& err);
752
+ void set_option(close_fds&& cfds);
736
753
 
737
754
  private:
738
755
  Popen* popen_ = nullptr;
@@ -924,6 +941,8 @@ private:
924
941
  * Command provided in a single string.
925
942
  * wait() - Wait for the child to exit.
926
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.
927
946
  * send(...) - Send input to the input channel of the child.
928
947
  * communicate(...) - Get the output/error from the child and close the channels
929
948
  * from the parent side.
@@ -974,6 +993,12 @@ public:
974
993
 
975
994
  int wait() noexcept(false);
976
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
+
977
1002
  void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
978
1003
 
979
1004
  void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
@@ -1026,6 +1051,8 @@ private:
1026
1051
  std::future<void> cleanup_future_;
1027
1052
  #endif
1028
1053
 
1054
+ bool close_fds_ = false;
1055
+
1029
1056
  std::string exe_name_;
1030
1057
 
1031
1058
  // Command in string format
@@ -1034,6 +1061,7 @@ private:
1034
1061
  std::vector<std::string> vargs_;
1035
1062
  std::vector<char*> cargv_;
1036
1063
 
1064
+ bool child_created_ = false;
1037
1065
  // Pid of the child process
1038
1066
  int child_pid_ = -1;
1039
1067
 
@@ -1093,6 +1121,68 @@ inline int Popen::wait() noexcept(false)
1093
1121
  #endif
1094
1122
  }
1095
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
+
1096
1186
  inline void Popen::execute_process() noexcept(false)
1097
1187
  {
1098
1188
  #ifdef __USING_WINDOWS__
@@ -1200,6 +1290,8 @@ inline void Popen::execute_process() noexcept(false)
1200
1290
  throw OSError("fork failed", errno);
1201
1291
  }
1202
1292
 
1293
+ child_created_ = true;
1294
+
1203
1295
  if (child_pid_ == 0)
1204
1296
  {
1205
1297
  // Close descriptors belonging to parent
@@ -1274,6 +1366,14 @@ namespace detail {
1274
1366
  if (err.rd_ch_ != -1) popen_->stream_.err_read_ = err.rd_ch_;
1275
1367
  }
1276
1368
 
1369
+ inline void ArgumentDeducer::set_option(close_fds&& cfds) {
1370
+ popen_->close_fds_ = cfds.close_all;
1371
+ }
1372
+
1373
+ #ifndef __USING_WINDOWS__
1374
+ void subprocess_close_all_fds(int except_fd);
1375
+ #endif
1376
+
1277
1377
 
1278
1378
  inline void Child::execute_child() {
1279
1379
  #ifndef __USING_WINDOWS__
@@ -1320,6 +1420,11 @@ namespace detail {
1320
1420
  if (stream.err_write_ != -1 && stream.err_write_ > 2)
1321
1421
  subprocess_close(stream.err_write_);
1322
1422
 
1423
+ // Close all the inherited fd's except the error write pipe
1424
+ if (parent_->close_fds_) {
1425
+ subprocess_close_all_fds(/*except_fd=*/ err_wr_pipe_);
1426
+ }
1427
+
1323
1428
  // Replace the current image with the executable
1324
1429
  sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
1325
1430