passenger 4.0.42 → 4.0.43

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (67) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +13 -0
  5. data/CONTRIBUTING.md +2 -19
  6. data/build/agents.rb +4 -1
  7. data/build/cxx_tests.rb +7 -2
  8. data/build/debian.rb +1 -1
  9. data/debian.template/control.template +0 -2
  10. data/doc/CodingTipsAndPitfalls.md +56 -0
  11. data/doc/Users guide Apache.idmap.txt +16 -14
  12. data/doc/Users guide Nginx.idmap.txt +8 -6
  13. data/doc/Users guide Standalone.idmap.txt +3 -1
  14. data/doc/Users guide Standalone.txt +1 -1
  15. data/doc/users_guide_snippets/environment_variables.txt +1 -0
  16. data/doc/users_guide_snippets/installation.txt +5 -5
  17. data/doc/users_guide_snippets/support_information.txt +42 -9
  18. data/doc/users_guide_snippets/troubleshooting/default.txt +42 -0
  19. data/ext/common/ApplicationPool2/Common.h +1 -0
  20. data/ext/common/ApplicationPool2/DirectSpawner.h +2 -7
  21. data/ext/common/ApplicationPool2/DummySpawner.h +1 -1
  22. data/ext/common/ApplicationPool2/Group.h +4 -2
  23. data/ext/common/ApplicationPool2/Options.h +9 -7
  24. data/ext/common/ApplicationPool2/Pool.h +83 -40
  25. data/ext/common/ApplicationPool2/Process.h +2 -6
  26. data/ext/common/ApplicationPool2/README.md +0 -40
  27. data/ext/common/ApplicationPool2/SmartSpawner.h +2 -9
  28. data/ext/common/ApplicationPool2/Spawner.h +1 -4
  29. data/ext/common/ApplicationPool2/SpawnerFactory.h +6 -9
  30. data/ext/common/ApplicationPool2/SuperGroup.h +3 -3
  31. data/ext/common/Constants.h +1 -1
  32. data/ext/common/UnionStation/Connection.h +227 -0
  33. data/ext/common/UnionStation/Core.h +497 -0
  34. data/ext/common/UnionStation/ScopeLog.h +172 -0
  35. data/ext/common/UnionStation/Transaction.h +276 -0
  36. data/ext/common/Utils.cpp +83 -8
  37. data/ext/common/Utils.h +25 -4
  38. data/ext/common/Utils/AnsiColorConstants.h +1 -0
  39. data/ext/common/Utils/ProcessMetricsCollector.h +6 -170
  40. data/ext/common/Utils/SpeedMeter.h +258 -0
  41. data/ext/common/Utils/StrIntUtils.cpp +6 -0
  42. data/ext/common/Utils/StringScanning.h +277 -0
  43. data/ext/common/Utils/SystemMetricsCollector.h +1460 -0
  44. data/ext/common/agents/Base.cpp +8 -8
  45. data/ext/common/agents/HelperAgent/Main.cpp +12 -6
  46. data/ext/common/agents/HelperAgent/RequestHandler.h +15 -16
  47. data/ext/common/agents/HelperAgent/SystemMetricsTool.cpp +199 -0
  48. data/ext/common/agents/LoggingAgent/LoggingServer.h +2 -1
  49. data/ext/common/agents/SpawnPreparer.cpp +20 -32
  50. data/lib/phusion_passenger.rb +1 -1
  51. data/lib/phusion_passenger/config/list_instances_command.rb +118 -0
  52. data/lib/phusion_passenger/config/main.rb +22 -4
  53. data/lib/phusion_passenger/config/system_metrics_command.rb +37 -0
  54. data/lib/phusion_passenger/config/utils.rb +1 -1
  55. data/lib/phusion_passenger/loader_shared_helpers.rb +8 -5
  56. data/lib/phusion_passenger/platform_info/compiler.rb +1 -1
  57. data/resources/templates/error_layout.html.template +3 -3
  58. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +3 -5
  59. data/test/cxx/ApplicationPool2/PoolTest.cpp +1 -3
  60. data/test/cxx/ApplicationPool2/ProcessTest.cpp +4 -4
  61. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +5 -7
  62. data/test/cxx/RequestHandlerTest.cpp +9 -3
  63. data/test/cxx/UnionStationTest.cpp +61 -64
  64. metadata +13 -4
  65. metadata.gz.asc +7 -7
  66. data/ext/common/UnionStation.h +0 -968
  67. data/helper-scripts/system-memory-stats.py +0 -207
@@ -0,0 +1,172 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2010-2014 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_UNION_STATION_SCOPE_LOG_H_
26
+ #define _PASSENGER_UNION_STATION_SCOPE_LOG_H_
27
+
28
+ #include <boost/noncopyable.hpp>
29
+
30
+ #include <sys/resource.h>
31
+
32
+ #include <string>
33
+
34
+ #include <StaticString.h>
35
+ #include <Exceptions.h>
36
+ #include <UnionStation/Transaction.h>
37
+ #include <Utils/StrIntUtils.h>
38
+ #include <Utils/SystemTime.h>
39
+
40
+ namespace Passenger {
41
+ namespace UnionStation {
42
+
43
+ using namespace std;
44
+ using namespace boost;
45
+
46
+
47
+ class ScopeLog: public noncopyable {
48
+ private:
49
+ Transaction * const transaction;
50
+ union {
51
+ const char *name;
52
+ struct {
53
+ const char *endMessage;
54
+ const char *abortMessage;
55
+ } granular;
56
+ } data;
57
+ enum {
58
+ NAME,
59
+ GRANULAR
60
+ } type: 1;
61
+ bool ok;
62
+
63
+ static string timevalToString(struct timeval &tv) {
64
+ unsigned long long i = (unsigned long long) tv.tv_sec * 1000000 + tv.tv_usec;
65
+ return usecToString(i);
66
+ }
67
+
68
+ static string usecToString(unsigned long long usec) {
69
+ char timestamp[2 * sizeof(unsigned long long) + 1];
70
+ integerToHexatri<unsigned long long>(usec, timestamp);
71
+ return timestamp;
72
+ }
73
+
74
+ public:
75
+ ScopeLog()
76
+ : transaction(NULL)
77
+ { }
78
+
79
+ ScopeLog(const TransactionPtr &_transaction, const char *name)
80
+ : transaction(_transaction.get())
81
+ {
82
+ type = NAME;
83
+ data.name = name;
84
+ ok = false;
85
+
86
+ char message[150];
87
+ char *pos = message;
88
+ const char *end = message + sizeof(message);
89
+ struct rusage usage;
90
+
91
+ pos = appendData(pos, end, "BEGIN: ");
92
+ pos = appendData(pos, end, name);
93
+ pos = appendData(pos, end, " (");
94
+ pos = appendData(pos, end, usecToString(SystemTime::getUsec()));
95
+ pos = appendData(pos, end, ",");
96
+ if (getrusage(RUSAGE_SELF, &usage) == -1) {
97
+ int e = errno;
98
+ throw SystemException("getrusage() failed", e);
99
+ }
100
+ pos = appendData(pos, end, timevalToString(usage.ru_utime));
101
+ pos = appendData(pos, end, ",");
102
+ pos = appendData(pos, end, timevalToString(usage.ru_stime));
103
+ pos = appendData(pos, end, ") ");
104
+
105
+ if (transaction != NULL) {
106
+ transaction->message(StaticString(message, pos - message));
107
+ }
108
+ }
109
+
110
+ ScopeLog(const TransactionPtr &_transaction,
111
+ const char *beginMessage,
112
+ const char *endMessage,
113
+ const char *abortMessage = NULL)
114
+ : transaction(_transaction.get())
115
+ {
116
+ if (_transaction != NULL) {
117
+ type = GRANULAR;
118
+ data.granular.endMessage = endMessage;
119
+ data.granular.abortMessage = abortMessage;
120
+ ok = abortMessage == NULL;
121
+ _transaction->message(beginMessage);
122
+ }
123
+ }
124
+
125
+ ~ScopeLog() {
126
+ if (transaction == NULL) {
127
+ return;
128
+ }
129
+ if (type == NAME) {
130
+ char message[150];
131
+ char *pos = message;
132
+ const char *end = message + sizeof(message);
133
+ struct rusage usage;
134
+
135
+ if (ok) {
136
+ pos = appendData(pos, end, "END: ");
137
+ } else {
138
+ pos = appendData(pos, end, "FAIL: ");
139
+ }
140
+ pos = appendData(pos, end, data.name);
141
+ pos = appendData(pos, end, " (");
142
+ pos = appendData(pos, end, usecToString(SystemTime::getUsec()));
143
+ pos = appendData(pos, end, ",");
144
+ if (getrusage(RUSAGE_SELF, &usage) == -1) {
145
+ int e = errno;
146
+ throw SystemException("getrusage() failed", e);
147
+ }
148
+ pos = appendData(pos, end, timevalToString(usage.ru_utime));
149
+ pos = appendData(pos, end, ",");
150
+ pos = appendData(pos, end, timevalToString(usage.ru_stime));
151
+ pos = appendData(pos, end, ")");
152
+
153
+ transaction->message(StaticString(message, pos - message));
154
+ } else {
155
+ if (ok) {
156
+ transaction->message(data.granular.endMessage);
157
+ } else {
158
+ transaction->message(data.granular.abortMessage);
159
+ }
160
+ }
161
+ }
162
+
163
+ void success() {
164
+ ok = true;
165
+ }
166
+ };
167
+
168
+
169
+ } // namespace UnionStation
170
+ } // namespace Passenger
171
+
172
+ #endif /* _PASSENGER_UNION_STATION_SCOPE_LOG_H_ */
@@ -0,0 +1,276 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2010-2014 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_UNION_STATION_TRANSACTION_H_
26
+ #define _PASSENGER_UNION_STATION_TRANSACTION_H_
27
+
28
+ #include <boost/shared_ptr.hpp>
29
+ #include <boost/noncopyable.hpp>
30
+ #include <oxt/backtrace.hpp>
31
+
32
+ #include <string>
33
+ #include <stdexcept>
34
+
35
+ #include <cstdio>
36
+ #include <cassert>
37
+
38
+ #include <Logging.h>
39
+ #include <Exceptions.h>
40
+ #include <StaticString.h>
41
+ #include <UnionStation/Connection.h>
42
+ #include <Utils/IOUtils.h>
43
+ #include <Utils/SystemTime.h>
44
+ #include <Utils/StrIntUtils.h>
45
+
46
+ namespace Passenger {
47
+ namespace UnionStation {
48
+
49
+ using namespace std;
50
+ using namespace boost;
51
+
52
+
53
+ enum ExceptionHandlingMode {
54
+ PRINT,
55
+ THROW,
56
+ IGNORE
57
+ };
58
+
59
+
60
+ class Core;
61
+ typedef boost::shared_ptr<Core> CorePtr;
62
+
63
+ inline void _checkinConnection(const CorePtr &core, const ConnectionPtr &connection);
64
+
65
+
66
+ class Transaction: public boost::noncopyable {
67
+ private:
68
+ static const int INT64_STR_BUFSIZE = 22; // Long enough for a 64-bit number.
69
+ static const unsigned long long IO_TIMEOUT = 5000000; // In microseconds.
70
+
71
+ const CorePtr core;
72
+ const ConnectionPtr connection;
73
+ const string txnId;
74
+ const string groupName;
75
+ const string category;
76
+ const string unionStationKey;
77
+ const ExceptionHandlingMode exceptionHandlingMode;
78
+ bool shouldFlushToDiskAfterClose;
79
+
80
+ /**
81
+ * Buffer must be at least txnId.size() + 1 + INT64_STR_BUFSIZE + 1 bytes.
82
+ */
83
+ char *insertTxnIdAndTimestamp(char *buffer, const char *end) {
84
+ assert(end - buffer >= int(txnId.size() + 1 + INT64_STR_BUFSIZE + 1));
85
+ int size;
86
+
87
+ // "txn-id-here"
88
+ buffer = appendData(buffer, end, txnId);
89
+
90
+ // "txn-id-here "
91
+ buffer = appendData(buffer, end, " ", 1);
92
+
93
+ // "txn-id-here 123456"
94
+ assert(end - buffer >= INT64_STR_BUFSIZE);
95
+ size = snprintf(buffer, INT64_STR_BUFSIZE, "%llu", SystemTime::getUsec());
96
+ if (size >= INT64_STR_BUFSIZE) {
97
+ // The buffer is too small.
98
+ throw IOException("Cannot format a new transaction log message timestamp.");
99
+ }
100
+ buffer += size;
101
+
102
+ // "txn-id-here 123456 "
103
+ buffer = appendData(buffer, end, " ", 1);
104
+
105
+ return buffer;
106
+ }
107
+
108
+ template<typename ExceptionType>
109
+ void handleException(const ExceptionType &e) {
110
+ switch (exceptionHandlingMode) {
111
+ case THROW:
112
+ throw e;
113
+ case PRINT: {
114
+ const tracable_exception *te =
115
+ dynamic_cast<const tracable_exception *>(&e);
116
+ if (te != NULL) {
117
+ P_WARN(te->what() << "\n" << te->backtrace());
118
+ } else {
119
+ P_WARN(e.what());
120
+ }
121
+ break;
122
+ }
123
+ default:
124
+ break;
125
+ }
126
+ }
127
+
128
+ public:
129
+ Transaction()
130
+ : exceptionHandlingMode(PRINT)
131
+ { }
132
+
133
+ Transaction(const CorePtr &_core,
134
+ const ConnectionPtr &_connection,
135
+ const string &_txnId,
136
+ const string &_groupName,
137
+ const string &_category,
138
+ const string &_unionStationKey,
139
+ ExceptionHandlingMode _exceptionHandlingMode = PRINT)
140
+ : core(_core),
141
+ connection(_connection),
142
+ txnId(_txnId),
143
+ groupName(_groupName),
144
+ category(_category),
145
+ unionStationKey(_unionStationKey),
146
+ exceptionHandlingMode(_exceptionHandlingMode),
147
+ shouldFlushToDiskAfterClose(false)
148
+ { }
149
+
150
+ ~Transaction() {
151
+ TRACE_POINT();
152
+ if (connection == NULL) {
153
+ return;
154
+ }
155
+ ConnectionLock l(connection);
156
+ if (!connection->connected()) {
157
+ return;
158
+ }
159
+
160
+ char timestamp[2 * sizeof(unsigned long long) + 1];
161
+ integerToHexatri<unsigned long long>(SystemTime::getUsec(),
162
+ timestamp);
163
+
164
+ UPDATE_TRACE_POINT();
165
+ ConnectionGuard guard(connection.get());
166
+ try {
167
+ unsigned long long timeout = IO_TIMEOUT;
168
+ writeArrayMessage(connection->fd, &timeout,
169
+ "closeTransaction",
170
+ txnId.c_str(),
171
+ timestamp,
172
+ NULL);
173
+
174
+ if (shouldFlushToDiskAfterClose) {
175
+ UPDATE_TRACE_POINT();
176
+ timeout = IO_TIMEOUT;
177
+ writeArrayMessage(connection->fd, &timeout,
178
+ "flush", NULL);
179
+ readArrayMessage(connection->fd, &timeout);
180
+ }
181
+
182
+ _checkinConnection(core, connection);
183
+ guard.clear();
184
+ } catch (const SystemException &e) {
185
+ string errorResponse;
186
+
187
+ UPDATE_TRACE_POINT();
188
+ guard.clear();
189
+ if (connection->disconnect(errorResponse)) {
190
+ handleException(IOException(
191
+ "Logging agent disconnected with error: " +
192
+ errorResponse));
193
+ } else {
194
+ handleException(e);
195
+ }
196
+ }
197
+ }
198
+
199
+ void message(const StaticString &text) {
200
+ TRACE_POINT();
201
+ if (connection == NULL) {
202
+ P_TRACE(3, "[Union Station log to null] " << text);
203
+ return;
204
+ }
205
+ ConnectionLock l(connection);
206
+ if (!connection->connected()) {
207
+ P_TRACE(3, "[Union Station log to null] " << text);
208
+ return;
209
+ }
210
+
211
+ char timestamp[2 * sizeof(unsigned long long) + 1];
212
+ integerToHexatri<unsigned long long>(SystemTime::getUsec(), timestamp);
213
+
214
+ UPDATE_TRACE_POINT();
215
+ ConnectionGuard guard(connection.get());
216
+ try {
217
+ unsigned long long timeout = IO_TIMEOUT;
218
+ P_TRACE(3, "[Union Station log] " << txnId << " " << timestamp << " " << text);
219
+ writeArrayMessage(connection->fd, &timeout,
220
+ "log",
221
+ txnId.c_str(),
222
+ timestamp,
223
+ NULL);
224
+ writeScalarMessage(connection->fd, text, &timeout);
225
+ guard.clear();
226
+ } catch (const std::exception &e) {
227
+ string errorResponse;
228
+
229
+ UPDATE_TRACE_POINT();
230
+ guard.clear();
231
+ if (connection->disconnect(errorResponse)) {
232
+ handleException(IOException(
233
+ "Logging agent disconnected with error: " +
234
+ errorResponse));
235
+ } else {
236
+ handleException(e);
237
+ }
238
+ }
239
+ }
240
+
241
+ void abort(const StaticString &text) {
242
+ message("ABORT");
243
+ }
244
+
245
+ void flushToDiskAfterClose(bool value) {
246
+ shouldFlushToDiskAfterClose = value;
247
+ }
248
+
249
+ bool isNull() const {
250
+ return connection == NULL;
251
+ }
252
+
253
+ const string &getTxnId() const {
254
+ return txnId;
255
+ }
256
+
257
+ const string &getGroupName() const {
258
+ return groupName;
259
+ }
260
+
261
+ const string &getCategory() const {
262
+ return category;
263
+ }
264
+
265
+ const string &getUnionStationKey() const {
266
+ return unionStationKey;
267
+ }
268
+ };
269
+
270
+ typedef boost::shared_ptr<Transaction> TransactionPtr;
271
+
272
+
273
+ } // namespace UnionStation
274
+ } // namespace Passenger
275
+
276
+ #endif /* _PASSENGER_UNION_STATION_TRANSACTION_H_ */
@@ -55,6 +55,7 @@
55
55
  #include <Utils/Base64.h>
56
56
  #include <Utils/CachedFileStat.hpp>
57
57
  #include <Utils/StrIntUtils.h>
58
+ #include <Utils/IOUtils.h>
58
59
  #include <Utils/HttpHeaderBufferer.h>
59
60
 
60
61
  #ifndef HOST_NAME_MAX
@@ -338,8 +339,8 @@ extractBaseName(const StaticString &path) {
338
339
  }
339
340
 
340
341
  string
341
- escapeForXml(const string &input) {
342
- string result(input);
342
+ escapeForXml(const StaticString &input) {
343
+ string result(input.data(), input.size());
343
344
  string::size_type input_pos = 0;
344
345
  string::size_type input_end_pos = input.size();
345
346
  string::size_type result_pos = 0;
@@ -994,6 +995,74 @@ runShellCommand(const StaticString &command) {
994
995
  }
995
996
  }
996
997
 
998
+ string
999
+ runCommandAndCaptureOutput(const char **command) {
1000
+ pid_t pid;
1001
+ int e;
1002
+ Pipe p;
1003
+
1004
+ p = createPipe();
1005
+
1006
+ this_thread::disable_syscall_interruption dsi;
1007
+ pid = syscalls::fork();
1008
+ if (pid == 0) {
1009
+ // Make ps nicer, we want to have as little impact on the rest
1010
+ // of the system as possible while collecting the metrics.
1011
+ int prio = getpriority(PRIO_PROCESS, getpid());
1012
+ prio++;
1013
+ if (prio > 20) {
1014
+ prio = 20;
1015
+ }
1016
+ setpriority(PRIO_PROCESS, getpid(), prio);
1017
+
1018
+ dup2(p[1], 1);
1019
+ close(p[0]);
1020
+ close(p[1]);
1021
+ closeAllFileDescriptors(2);
1022
+ execvp(command[0], (char * const *) command);
1023
+ _exit(1);
1024
+ } else if (pid == -1) {
1025
+ e = errno;
1026
+ throw SystemException("Cannot fork() a new process", e);
1027
+ } else {
1028
+ bool done = false;
1029
+ string result;
1030
+
1031
+ p[1].close();
1032
+ while (!done) {
1033
+ char buf[1024 * 4];
1034
+ ssize_t ret;
1035
+
1036
+ try {
1037
+ this_thread::restore_syscall_interruption rsi(dsi);
1038
+ ret = syscalls::read(p[0], buf, sizeof(buf));
1039
+ } catch (const thread_interrupted &) {
1040
+ syscalls::kill(SIGKILL, pid);
1041
+ syscalls::waitpid(pid, NULL, 0);
1042
+ throw;
1043
+ }
1044
+ if (ret == -1) {
1045
+ e = errno;
1046
+ syscalls::kill(SIGKILL, pid);
1047
+ syscalls::waitpid(pid, NULL, 0);
1048
+ throw SystemException(string("Cannot read output from the '") +
1049
+ command[1] + "' command", e);
1050
+ }
1051
+ done = ret == 0;
1052
+ result.append(buf, ret);
1053
+ }
1054
+ p[0].close();
1055
+ syscalls::waitpid(pid, NULL, 0);
1056
+
1057
+ if (result.empty()) {
1058
+ throw RuntimeException(string("The '") + command[1] +
1059
+ "' command failed");
1060
+ } else {
1061
+ return result;
1062
+ }
1063
+ }
1064
+ }
1065
+
997
1066
  #ifdef __APPLE__
998
1067
  // http://www.opensource.apple.com/source/Libc/Libc-825.26/sys/fork.c
999
1068
  // This bypasses atfork handlers.
@@ -1054,7 +1123,7 @@ getFileDescriptorLimit() {
1054
1123
  // descriptor that the process is currently using.
1055
1124
  // See also http://stackoverflow.com/questions/899038/getting-the-highest-allocated-file-descriptor
1056
1125
  static int
1057
- getHighestFileDescriptor() {
1126
+ getHighestFileDescriptor(bool asyncSignalSafe) {
1058
1127
  #if defined(F_MAXFD)
1059
1128
  int ret;
1060
1129
 
@@ -1099,9 +1168,15 @@ getHighestFileDescriptor() {
1099
1168
  goto done;
1100
1169
  }
1101
1170
 
1102
- do {
1103
- pid = asyncFork();
1104
- } while (pid == -1 && errno == EINTR);
1171
+ if (asyncSignalSafe) {
1172
+ do {
1173
+ pid = asyncFork();
1174
+ } while (pid == -1 && errno == EINTR);
1175
+ } else {
1176
+ do {
1177
+ pid = fork();
1178
+ } while (pid == -1 && errno == EINTR);
1179
+ }
1105
1180
 
1106
1181
  if (pid == 0) {
1107
1182
  // Don't close p[0] here or it might affect the result.
@@ -1243,7 +1318,7 @@ done:
1243
1318
  }
1244
1319
 
1245
1320
  void
1246
- closeAllFileDescriptors(int lastToKeepOpen) {
1321
+ closeAllFileDescriptors(int lastToKeepOpen, bool asyncSignalSafe) {
1247
1322
  #if defined(F_CLOSEM)
1248
1323
  int ret;
1249
1324
  do {
@@ -1257,7 +1332,7 @@ closeAllFileDescriptors(int lastToKeepOpen) {
1257
1332
  return;
1258
1333
  #endif
1259
1334
 
1260
- for (int i = getHighestFileDescriptor(); i > lastToKeepOpen; i--) {
1335
+ for (int i = getHighestFileDescriptor(asyncSignalSafe); i > lastToKeepOpen; i--) {
1261
1336
  /* Even though we normally shouldn't retry on EINTR
1262
1337
  * (http://news.ycombinator.com/item?id=3363819)
1263
1338
  * it's okay to do that here because because this function