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
@@ -65,10 +65,6 @@ private:
65
65
  BufferedIO io;
66
66
  };
67
67
 
68
- /** The event loop that created Process objects should use, and that I/O forwarding
69
- * functions should use. For example data on the error pipe is forwarded using this event loop.
70
- */
71
- SafeLibevPtr libev;
72
68
  const vector<string> preloaderCommand;
73
69
  map<string, string> preloaderAnnotations;
74
70
  Options options;
@@ -703,14 +699,12 @@ protected:
703
699
  }
704
700
 
705
701
  public:
706
- SmartSpawner(const SafeLibevPtr &_libev,
707
- const ResourceLocator &_resourceLocator,
702
+ SmartSpawner(const ResourceLocator &_resourceLocator,
708
703
  const ServerInstanceDir::GenerationPtr &_generation,
709
704
  const vector<string> &_preloaderCommand,
710
705
  const Options &_options,
711
706
  const SpawnerConfigPtr &_config = SpawnerConfigPtr())
712
707
  : Spawner(_resourceLocator),
713
- libev(_libev),
714
708
  preloaderCommand(_preloaderCommand)
715
709
  {
716
710
  if (preloaderCommand.size() < 2) {
@@ -718,7 +712,7 @@ public:
718
712
  }
719
713
 
720
714
  generation = _generation;
721
- options = _options.copyAndPersist().clearLogger();
715
+ options = _options.copyAndPersist().detachFromUnionStationTransaction();
722
716
  pid = -1;
723
717
  m_lastUsed = SystemTime::getUsec();
724
718
 
@@ -768,7 +762,6 @@ public:
768
762
  UPDATE_TRACE_POINT();
769
763
  NegotiationDetails details;
770
764
  details.preparation = &preparation;
771
- details.libev = libev;
772
765
  details.pid = result.pid;
773
766
  details.adminSocket = result.adminSocket;
774
767
  details.io = result.io;
@@ -77,7 +77,6 @@
77
77
  #include <ApplicationPool2/Options.h>
78
78
  #include <ApplicationPool2/PipeWatcher.h>
79
79
  #include <FileDescriptor.h>
80
- #include <SafeLibev.h>
81
80
  #include <Exceptions.h>
82
81
  #include <ResourceLocator.h>
83
82
  #include <StaticString.h>
@@ -334,8 +333,6 @@ protected:
334
333
  * by security validators to check whether the information sent back by the
335
334
  * process make any sense. */
336
335
  SpawnPreparationInfo *preparation;
337
- /** The SafeLibev that the returned Process should be initialized with. */
338
- SafeLibevPtr libev;
339
336
  /** This object captures the process's stderr while negotiation is in progress.
340
337
  * (Recall that negotiation is performed over the process's stdout while stderr
341
338
  * is used purely for outputting messages.)
@@ -530,7 +527,7 @@ private:
530
527
  }
531
528
 
532
529
  ProcessPtr process = boost::make_shared<Process>(
533
- details.libev, details.pid,
530
+ details.pid,
534
531
  details.gupid, details.connectPassword,
535
532
  details.adminSocket, details.errorPipe,
536
533
  sockets, creationTime, details.spawnStartTime,
@@ -40,7 +40,6 @@ using namespace oxt;
40
40
 
41
41
  class SpawnerFactory {
42
42
  private:
43
- SafeLibevPtr libev;
44
43
  ResourceLocator resourceLocator;
45
44
  ServerInstanceDir::GenerationPtr generation;
46
45
  RandomGeneratorPtr randomGenerator;
@@ -60,17 +59,15 @@ private:
60
59
  } else {
61
60
  return SpawnerPtr();
62
61
  }
63
- return boost::make_shared<SmartSpawner>(libev, resourceLocator,
62
+ return boost::make_shared<SmartSpawner>(resourceLocator,
64
63
  generation, preloaderCommand, options, config);
65
64
  }
66
65
 
67
66
  public:
68
- SpawnerFactory(const SafeLibevPtr &_libev,
69
- const ResourceLocator &_resourceLocator,
67
+ SpawnerFactory(const ResourceLocator &_resourceLocator,
70
68
  const ServerInstanceDir::GenerationPtr &_generation,
71
69
  const SpawnerConfigPtr &_config = SpawnerConfigPtr())
72
- : libev(_libev),
73
- resourceLocator(_resourceLocator),
70
+ : resourceLocator(_resourceLocator),
74
71
  generation(_generation)
75
72
  {
76
73
  if (_config == NULL) {
@@ -86,13 +83,13 @@ public:
86
83
  if (options.spawnMethod == "smart" || options.spawnMethod == "smart-lv2") {
87
84
  SpawnerPtr spawner = tryCreateSmartSpawner(options);
88
85
  if (spawner == NULL) {
89
- spawner = boost::make_shared<DirectSpawner>(libev,
90
- resourceLocator, generation, config);
86
+ spawner = boost::make_shared<DirectSpawner>(resourceLocator,
87
+ generation, config);
91
88
  }
92
89
  return spawner;
93
90
  } else if (options.spawnMethod == "direct" || options.spawnMethod == "conservative") {
94
91
  boost::shared_ptr<DirectSpawner> spawner = boost::make_shared<DirectSpawner>(
95
- libev, resourceLocator, generation, config);
92
+ resourceLocator, generation, config);
96
93
  return spawner;
97
94
  } else if (options.spawnMethod == "dummy") {
98
95
  syscalls::usleep(config->spawnerCreationSleepTime);
@@ -420,7 +420,7 @@ public:
420
420
  SuperGroup(const PoolPtr &_pool, const Options &options)
421
421
  : pool(_pool)
422
422
  {
423
- this->options = options.copyAndPersist().clearLogger();
423
+ this->options = options.copyAndPersist().detachFromUnionStationTransaction();
424
424
  this->name = options.getAppGroupName();
425
425
  secret = generateSecret();
426
426
  state = INITIALIZING;
@@ -605,7 +605,7 @@ public:
605
605
  boost::bind(
606
606
  doInitialize,
607
607
  shared_from_this(),
608
- newOptions.copyAndPersist().clearLogger(),
608
+ newOptions.copyAndPersist().detachFromUnionStationTransaction(),
609
609
  generation),
610
610
  "SuperGroup initializer: " + name,
611
611
  POOL_HELPER_THREAD_STACK_SIZE);
@@ -656,7 +656,7 @@ public:
656
656
  doRestart,
657
657
  // Keep reference to self to prevent destruction.
658
658
  shared_from_this(),
659
- options.copyAndPersist().clearLogger(),
659
+ options.copyAndPersist().detachFromUnionStationTransaction(),
660
660
  generation),
661
661
  "SuperGroup restarter: " + name,
662
662
  POOL_HELPER_THREAD_STACK_SIZE);
@@ -88,7 +88,7 @@
88
88
 
89
89
  #define NGINX_DOC_URL "http://www.modrails.com/documentation/Users%20guide%20Nginx.html"
90
90
 
91
- #define PASSENGER_VERSION "4.0.42"
91
+ #define PASSENGER_VERSION "4.0.43"
92
92
 
93
93
  #define POOL_HELPER_THREAD_STACK_SIZE 262144
94
94
 
@@ -0,0 +1,227 @@
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_CONNECTION_H_
26
+ #define _PASSENGER_UNION_STATION_CONNECTION_H_
27
+
28
+ #include <boost/thread.hpp>
29
+ #include <boost/shared_ptr.hpp>
30
+ #include <boost/noncopyable.hpp>
31
+ #include <oxt/system_calls.hpp>
32
+ #include <oxt/backtrace.hpp>
33
+
34
+ #include <string>
35
+ #include <vector>
36
+
37
+ #include <errno.h>
38
+
39
+ #include <Exceptions.h>
40
+ #include <Utils/IOUtils.h>
41
+ #include <Utils/MessageIO.h>
42
+
43
+ namespace Passenger {
44
+ namespace UnionStation {
45
+
46
+ using namespace std;
47
+ using namespace boost;
48
+
49
+
50
+ struct Connection;
51
+ typedef boost::shared_ptr<Connection> ConnectionPtr;
52
+
53
+ inline void _disconnectConnection(Connection *connection);
54
+
55
+
56
+ /**
57
+ * A scope guard which closes the given Connection on destruction unless cleared.
58
+ * Note that this class does not hold a shared_ptr to the Connection object,
59
+ * so make sure that the Connection outlives the guard object.
60
+ */
61
+ class ConnectionGuard: public boost::noncopyable {
62
+ private:
63
+ Connection * const connection;
64
+ bool cleared;
65
+
66
+ public:
67
+ ConnectionGuard(Connection *_connection)
68
+ : connection(_connection),
69
+ cleared(false)
70
+ { }
71
+
72
+ ~ConnectionGuard() {
73
+ if (!cleared) {
74
+ _disconnectConnection(connection);
75
+ }
76
+ }
77
+
78
+ void clear() {
79
+ cleared = true;
80
+ }
81
+ };
82
+
83
+
84
+ /**
85
+ * Represents a connection to the logging server.
86
+ * All access to the file descriptor must be synchronized through the syncher.
87
+ * You can use the ConnectionLock to do that.
88
+ */
89
+ struct Connection: public boost::noncopyable {
90
+ mutable boost::mutex syncher;
91
+ int fd;
92
+
93
+ Connection(int _fd)
94
+ : fd(_fd)
95
+ { }
96
+
97
+ ~Connection() {
98
+ disconnect();
99
+ }
100
+
101
+ bool connected() const {
102
+ return fd != -1;
103
+ }
104
+
105
+ /**
106
+ * Disconnect from the server. If the server sent an error response
107
+ * right before closing the connection, try to read it and return it
108
+ * through `errorResponse`. Returns whether an error response was read.
109
+ *
110
+ * Reading the error response might result in an exception, e.g.
111
+ * because of networking and protocol exceptions. In such an event,
112
+ * the connection is still guaranteed to be disconnected.
113
+ */
114
+ bool disconnect(string &errorResponse) {
115
+ if (!connected()) {
116
+ return false;
117
+ }
118
+
119
+ ConnectionGuard guard(this);
120
+
121
+ /* The server might send an "error" array message
122
+ * just before disconnecting. Try to read it.
123
+ */
124
+ TRACE_POINT();
125
+ vector<string> response;
126
+ try {
127
+ unsigned long long timeout = 20000000;
128
+ while (true) {
129
+ response = readArrayMessage(fd, &timeout);
130
+ }
131
+ } catch (const TimeoutException &) {
132
+ /* This means that the last message isn't an array
133
+ * message or that the server didn't send it quickly
134
+ * enough. In any case, discard whatever previous
135
+ * array messages we were able to read because they're
136
+ * guaranteed not to be the error message we're expecting.
137
+ */
138
+ response.clear();
139
+ } catch (const SystemException &e) {
140
+ /* We treat ECONNRESET the same as EOFException.
141
+ * Other errors are treated as TimeoutException.
142
+ */
143
+ if (e.code() != ECONNRESET) {
144
+ response.clear();
145
+ }
146
+ } catch (const EOFException &) {
147
+ /* Do nothing. We've successfully read the last array message. */
148
+ }
149
+
150
+ UPDATE_TRACE_POINT();
151
+ if (response.size() == 2 && response[0] == "error") {
152
+ errorResponse = response[1];
153
+ return true;
154
+ } else {
155
+ return false;
156
+ }
157
+ }
158
+
159
+ /** Disconnect from the server, ignoring any error responses the
160
+ * server might have sent.
161
+ */
162
+ void disconnect() {
163
+ if (fd != -1) {
164
+ this_thread::disable_interruption di;
165
+ this_thread::disable_syscall_interruption dsi;
166
+ safelyClose(fd);
167
+ fd = -1;
168
+ }
169
+ }
170
+ };
171
+
172
+ typedef boost::shared_ptr<Connection> ConnectionPtr;
173
+
174
+
175
+ /**
176
+ * A special lock type for Connection that also keeps a smart
177
+ * pointer to the data structure so that the mutex is not destroyed
178
+ * prematurely.
179
+ */
180
+ struct ConnectionLock: public boost::noncopyable {
181
+ ConnectionPtr connection;
182
+ bool locked;
183
+
184
+ ConnectionLock(const ConnectionPtr &c)
185
+ : connection(c)
186
+ {
187
+ c->syncher.lock();
188
+ locked = true;
189
+ }
190
+
191
+ ~ConnectionLock() {
192
+ if (locked) {
193
+ connection->syncher.unlock();
194
+ }
195
+ }
196
+
197
+ void reset(const ConnectionPtr &c, bool lockNow = true) {
198
+ if (locked) {
199
+ connection->syncher.unlock();
200
+ }
201
+ connection = c;
202
+ if (lockNow) {
203
+ connection->syncher.lock();
204
+ locked = true;
205
+ } else {
206
+ locked = false;
207
+ }
208
+ }
209
+
210
+ void lock() {
211
+ assert(!locked);
212
+ connection->syncher.lock();
213
+ locked = true;
214
+ }
215
+ };
216
+
217
+
218
+ inline void
219
+ _disconnectConnection(Connection *connection) {
220
+ connection->disconnect();
221
+ }
222
+
223
+
224
+ } // namespace UnionStation
225
+ } // namespace Passenger
226
+
227
+ #endif /* _PASSENGER_UNION_STATION_CONNECTION_H_ */
@@ -0,0 +1,497 @@
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_CORE_H_
26
+ #define _PASSENGER_UNION_STATION_CORE_H_
27
+
28
+ #include <boost/shared_ptr.hpp>
29
+ #include <boost/enable_shared_from_this.hpp>
30
+ #include <boost/thread.hpp>
31
+ #include <oxt/backtrace.hpp>
32
+
33
+ #include <errno.h>
34
+
35
+ #include <string>
36
+ #include <vector>
37
+ #include <stdexcept>
38
+
39
+ #include <Logging.h>
40
+ #include <Exceptions.h>
41
+ #include <RandomGenerator.h>
42
+ #include <StaticString.h>
43
+ #include <UnionStation/Connection.h>
44
+ #include <UnionStation/Transaction.h>
45
+ #include <Utils.h>
46
+ #include <Utils/MessageIO.h>
47
+ #include <Utils/SystemTime.h>
48
+
49
+ namespace Passenger {
50
+ namespace UnionStation {
51
+
52
+ using namespace std;
53
+ using namespace boost;
54
+
55
+
56
+ class Core: public boost::enable_shared_from_this<Core> {
57
+ private:
58
+ static const unsigned int CONNECTION_POOL_MAX_SIZE = 10;
59
+ static const unsigned int TXN_ID_MAX_SIZE =
60
+ 2 * sizeof(unsigned int) + // max hex timestamp size
61
+ 11 + // space for a random identifier
62
+ 1; // null terminator
63
+
64
+ /**** Server information ****/
65
+ const string serverAddress;
66
+ const string username;
67
+ const string password;
68
+ const string nodeName;
69
+
70
+ /**** Working objects ****/
71
+ RandomGenerator randomGenerator;
72
+ TransactionPtr nullTransaction;
73
+
74
+ /********************** Connection handling fields **********************
75
+ * These fields are synchronized through the mutex. The contents
76
+ * of the conntection objects are not synchronized through this mutex,
77
+ * but through the Connection object's own mutex.
78
+ ************************************************************************/
79
+ mutable boost::mutex syncher;
80
+ vector<ConnectionPtr> connectionPool;
81
+ /** How long to wait before reconnecting. */
82
+ unsigned long long reconnectTimeout;
83
+ /** Earliest time at which we should attempt a reconnect. Earlier attempts
84
+ * will fail. Calculated from reconnectTimeout.
85
+ */
86
+ unsigned long long nextReconnectTime;
87
+
88
+ static string determineNodeName(const string &givenNodeName) {
89
+ if (givenNodeName.empty()) {
90
+ return getHostName();
91
+ } else {
92
+ return givenNodeName;
93
+ }
94
+ }
95
+
96
+ static bool isNetworkError(int code) {
97
+ return code == EPIPE || code == ECONNREFUSED || code == ECONNRESET
98
+ || code == EHOSTUNREACH || code == ENETDOWN || code == ENETUNREACH
99
+ || code == ETIMEDOUT;
100
+ }
101
+
102
+ template<typename T>
103
+ static bool instanceof(const std::exception &e) {
104
+ return dynamic_cast<const T *>(&e) != NULL;
105
+ }
106
+
107
+ void initialize() {
108
+ nullTransaction = boost::make_shared<Transaction>();
109
+ reconnectTimeout = 1000000;
110
+ nextReconnectTime = 0;
111
+ }
112
+
113
+ /** Creates a transaction ID string. `txnId` MUST be at least TXN_ID_MAX_SIZE bytes. */
114
+ void createTxnId(char *txnId, char **txnIdEnd, unsigned long long timestamp) {
115
+ unsigned int timestampSize;
116
+ char *end;
117
+
118
+ // "[timestamp]"
119
+ // Our timestamp is like a Unix timestamp but with minutes
120
+ // resolution instead of seconds. 32 bits will last us for
121
+ // about 8000 years.
122
+ timestampSize = integerToHexatri<unsigned int>(
123
+ timestamp / 1000000 / 60,
124
+ txnId);
125
+ end = txnId + timestampSize;
126
+
127
+ // "[timestamp]-"
128
+ *end = '-';
129
+ end++;
130
+
131
+ // "[timestamp]-[random id]"
132
+ randomGenerator.generateAsciiString(end, 11);
133
+ end += 11;
134
+ *end = '\0';
135
+ *txnIdEnd = end;
136
+ }
137
+
138
+ ConnectionPtr createNewConnection() {
139
+ TRACE_POINT();
140
+ int fd;
141
+ vector<string> args;
142
+ unsigned long long timeout = 15000000;
143
+
144
+ // Create socket.
145
+ fd = connectToServer(serverAddress);
146
+ FdGuard guard(fd, true);
147
+
148
+ // Handshake: process protocol version number.
149
+ if (!readArrayMessage(fd, args, &timeout)) {
150
+ throw IOException("The logging agent closed the connection before sending a version identifier.");
151
+ }
152
+ if (args.size() != 2 || args[0] != "version") {
153
+ throw IOException("The logging agent server didn't sent a valid version identifier.");
154
+ }
155
+ if (args[1] != "1") {
156
+ string message = "Unsupported logging agent protocol version " +
157
+ args[1] + ".";
158
+ throw IOException(message);
159
+ }
160
+
161
+ // Handshake: authenticate.
162
+ UPDATE_TRACE_POINT();
163
+ writeScalarMessage(fd, username, &timeout);
164
+ writeScalarMessage(fd, password, &timeout);
165
+
166
+ UPDATE_TRACE_POINT();
167
+ if (!readArrayMessage(fd, args, &timeout)) {
168
+ throw IOException("The logging agent did not send an authentication response.");
169
+ } else if (args.size() != 1) {
170
+ throw IOException("The authentication response that the logging agent sent is not valid.");
171
+ } else if (args[0] != "ok") {
172
+ throw SecurityException("The logging agent server denied authentication: " + args[0]);
173
+ }
174
+
175
+ // Initialize session.
176
+ UPDATE_TRACE_POINT();
177
+ writeArrayMessage(fd, &timeout, "init", nodeName.c_str(), NULL);
178
+ if (!readArrayMessage(fd, args, &timeout)) {
179
+ throw SystemException("Cannot connect to logging server", ECONNREFUSED);
180
+ } else if (args.size() != 1) {
181
+ throw IOException("Logging server returned an invalid reply for the 'init' command");
182
+ } else if (args[0] == "server shutting down") {
183
+ throw SystemException("Cannot connect to server", ECONNREFUSED);
184
+ } else if (args[0] != "ok") {
185
+ throw IOException("Logging server returned an invalid reply for the 'init' command");
186
+ }
187
+
188
+ ConnectionPtr connection = boost::make_shared<Connection>(fd);
189
+ guard.clear();
190
+ return connection;
191
+ }
192
+
193
+ public:
194
+ Core() {
195
+ initialize();
196
+ }
197
+
198
+ Core(const string &_serverAddress, const string &_username,
199
+ const string &_password, const string &_nodeName = string())
200
+ : serverAddress(_serverAddress),
201
+ username(_username),
202
+ password(_password),
203
+ nodeName(determineNodeName(_nodeName))
204
+ {
205
+ initialize();
206
+ }
207
+
208
+
209
+ /***** Connection pool methods *****/
210
+
211
+ ConnectionPtr checkoutConnection() {
212
+ TRACE_POINT();
213
+ boost::unique_lock<boost::mutex> l(syncher);
214
+ if (!connectionPool.empty()) {
215
+ P_TRACE(3, "Checked out existing connection");
216
+ ConnectionPtr connection = connectionPool.back();
217
+ connectionPool.pop_back();
218
+ return connection;
219
+
220
+ } else {
221
+ if (SystemTime::getUsec() < nextReconnectTime) {
222
+ P_TRACE(3, "Not yet time to reconnect; returning NULL connection");
223
+ return ConnectionPtr();
224
+ }
225
+
226
+ l.unlock();
227
+ P_TRACE(3, "Creating new connection with logging agent");
228
+ ConnectionPtr connection;
229
+ try {
230
+ connection = createNewConnection();
231
+ } catch (const TimeoutException &) {
232
+ l.lock();
233
+ P_WARN("Timeout trying to connect to the logging agent at " << serverAddress << "; " <<
234
+ "will reconnect in " << reconnectTimeout / 1000000 << " second(s).");
235
+ nextReconnectTime = SystemTime::getUsec() + reconnectTimeout;
236
+ return ConnectionPtr();
237
+ } catch (const tracable_exception &e) {
238
+ l.lock();
239
+ nextReconnectTime = SystemTime::getUsec() + reconnectTimeout;
240
+ if (instanceof<IOException>(e) || instanceof<SystemException>(e)) {
241
+ P_WARN("Cannot connect to the logging agent at " << serverAddress <<
242
+ " (" << e.what() << "); will reconnect in " <<
243
+ reconnectTimeout / 1000000 << " second(s).");
244
+ return ConnectionPtr();
245
+ } else {
246
+ throw;
247
+ }
248
+ }
249
+
250
+ return connection;
251
+ }
252
+ }
253
+
254
+ void checkinConnection(const ConnectionPtr &connection) {
255
+ boost::unique_lock<boost::mutex> l(syncher);
256
+ if (connectionPool.size() < CONNECTION_POOL_MAX_SIZE) {
257
+ connectionPool.push_back(connection);
258
+ } else {
259
+ l.unlock();
260
+ connection->disconnect();
261
+ }
262
+ }
263
+
264
+
265
+ /***** Transaction methods *****/
266
+
267
+ TransactionPtr createNullTransaction() const {
268
+ return nullTransaction;
269
+ }
270
+
271
+ bool sendRequest(const ConnectionPtr &connection, StaticString args[],
272
+ unsigned int nargs, bool expectAck)
273
+ {
274
+ ConnectionLock cl(connection);
275
+ ConnectionGuard guard(connection.get());
276
+
277
+ try {
278
+ unsigned long long timeout = 15000000;
279
+
280
+ writeArrayMessage(connection->fd, args, nargs, &timeout);
281
+
282
+ if (expectAck) {
283
+ vector<string> args;
284
+ if (!readArrayMessage(connection->fd, args, &timeout)) {
285
+ boost::lock_guard<boost::mutex> l(syncher);
286
+ P_WARN("The logging agent at " << serverAddress <<
287
+ " closed the connection (no error message given);" <<
288
+ " will reconnect in " << reconnectTimeout / 1000000 <<
289
+ " second(s).");
290
+ nextReconnectTime = SystemTime::getUsec() + reconnectTimeout;
291
+ return false;
292
+ } else if (args.size() == 2 && args[0] == "error") {
293
+ boost::lock_guard<boost::mutex> l(syncher);
294
+ P_WARN("The logging agent at " << serverAddress <<
295
+ " closed the connection (error message: " << args[1] <<
296
+ "); will reconnect in " << reconnectTimeout / 1000000 <<
297
+ " second(s).");
298
+ nextReconnectTime = SystemTime::getUsec() + reconnectTimeout;
299
+ return false;
300
+ } else if (args.empty() || args[0] != "ok") {
301
+ boost::lock_guard<boost::mutex> l(syncher);
302
+ P_WARN("The logging agent at " << serverAddress <<
303
+ " sent an unexpected reply;" <<
304
+ " will reconnect in " << reconnectTimeout / 1000000 <<
305
+ " second(s).");
306
+ nextReconnectTime = SystemTime::getUsec() + reconnectTimeout;
307
+ return false;
308
+ }
309
+ }
310
+
311
+ guard.clear();
312
+ return true;
313
+
314
+ } catch (const TimeoutException &) {
315
+ boost::lock_guard<boost::mutex> l(syncher);
316
+ P_WARN("Timeout trying to communicate with the logging agent at " << serverAddress << "; " <<
317
+ "will reconnect in " << reconnectTimeout / 1000000 << " second(s).");
318
+ nextReconnectTime = SystemTime::getUsec() + reconnectTimeout;
319
+ return false;
320
+
321
+ } catch (const SystemException &e) {
322
+ if (e.code() == ENOENT || isNetworkError(e.code())) {
323
+ string errorResponse;
324
+ bool gotErrorResponse;
325
+
326
+ guard.clear();
327
+ gotErrorResponse = connection->disconnect(errorResponse);
328
+ boost::lock_guard<boost::mutex> l(syncher);
329
+ if (gotErrorResponse) {
330
+ P_WARN("The logging agent at " << serverAddress <<
331
+ " closed the connection (error message: " << errorResponse <<
332
+ "); will reconnect in " << reconnectTimeout / 1000000 <<
333
+ " second(s).");
334
+ } else {
335
+ P_WARN("The logging agent at " << serverAddress <<
336
+ " closed the connection (no error message given);" <<
337
+ " will reconnect in " << reconnectTimeout / 1000000 <<
338
+ " second(s).");
339
+ }
340
+ nextReconnectTime = SystemTime::getUsec() + reconnectTimeout;
341
+ return false;
342
+ } else {
343
+ throw;
344
+ }
345
+ }
346
+ }
347
+
348
+ TransactionPtr newTransaction(const string &groupName,
349
+ const string &category = "requests",
350
+ const string &unionStationKey = "-",
351
+ const string &filters = string())
352
+ {
353
+ if (isNull()) {
354
+ return createNullTransaction();
355
+ }
356
+
357
+ // Prepare parameters.
358
+ unsigned long long timestamp = SystemTime::getUsec();
359
+ char txnId[TXN_ID_MAX_SIZE], *txnIdEnd;
360
+ char timestampStr[2 * sizeof(unsigned long long) + 1];
361
+
362
+ createTxnId(txnId, &txnIdEnd, timestamp);
363
+ integerToHexatri<unsigned long long>(timestamp, timestampStr);
364
+
365
+ StaticString params[] = {
366
+ StaticString("openTransaction", sizeof("openTransaction") - 1),
367
+ StaticString(txnId, txnIdEnd - txnId),
368
+ groupName,
369
+ // empty nodeName, implies using the default
370
+ // nodeName passed during initialization
371
+ StaticString(),
372
+ category,
373
+ timestampStr,
374
+ unionStationKey,
375
+ StaticString("true", 4), // crashProtect
376
+ StaticString("true", 4), // ack
377
+ filters
378
+ };
379
+ unsigned int nparams = sizeof(params) / sizeof(StaticString);
380
+
381
+ // Get a connection to the logging server.
382
+ ConnectionPtr connection = checkoutConnection();
383
+ if (connection == NULL) {
384
+ return createNullTransaction();
385
+ }
386
+
387
+ // Send request, process reply.
388
+ if (sendRequest(connection, params, nparams, true)) {
389
+ ConnectionGuard guard(connection.get());
390
+ TransactionPtr transaction = boost::make_shared<Transaction>(
391
+ shared_from_this(),
392
+ connection,
393
+ string(txnId, txnIdEnd - txnId),
394
+ groupName,
395
+ category,
396
+ unionStationKey);
397
+ guard.clear();
398
+ return transaction;
399
+ } else {
400
+ return createNullTransaction();
401
+ }
402
+ }
403
+
404
+ TransactionPtr continueTransaction(const string &txnId,
405
+ const string &groupName,
406
+ const string &category = "requests",
407
+ const string &unionStationKey = "-")
408
+ {
409
+ if (isNull() || txnId.empty()) {
410
+ return createNullTransaction();
411
+ }
412
+
413
+ // Prepare parameters.
414
+ char timestampStr[2 * sizeof(unsigned long long) + 1];
415
+ integerToHexatri<unsigned long long>(SystemTime::getUsec(), timestampStr);
416
+
417
+ StaticString params[] = {
418
+ StaticString("openTransaction", sizeof("openTransaction") - 1),
419
+ txnId,
420
+ groupName,
421
+ // empty nodeName, implies using the default
422
+ // nodeName passed during initialization
423
+ StaticString(),
424
+ category,
425
+ timestampStr,
426
+ unionStationKey,
427
+ StaticString("true", 4), // crashProtect
428
+ StaticString("false", 4) // ack
429
+ };
430
+ unsigned int nparams = sizeof(params) / sizeof(StaticString);
431
+
432
+ // Get a connection to the logging server.
433
+ ConnectionPtr connection = checkoutConnection();
434
+ if (connection == NULL) {
435
+ return createNullTransaction();
436
+ }
437
+
438
+ // Send request.
439
+ if (sendRequest(connection, params, nparams, false)) {
440
+ ConnectionGuard guard(connection.get());
441
+ TransactionPtr transaction = boost::make_shared<Transaction>(
442
+ shared_from_this(),
443
+ connection,
444
+ txnId,
445
+ groupName,
446
+ category,
447
+ unionStationKey);
448
+ guard.clear();
449
+ return transaction;
450
+ } else {
451
+ return createNullTransaction();
452
+ }
453
+ }
454
+
455
+
456
+ /***** Parameter getters and setters *****/
457
+
458
+ void setReconnectTimeout(unsigned long long usec) {
459
+ boost::lock_guard<boost::mutex> l(syncher);
460
+ reconnectTimeout = usec;
461
+ }
462
+
463
+ bool isNull() const {
464
+ return serverAddress.empty();
465
+ }
466
+
467
+ const string &getAddress() const {
468
+ return serverAddress;
469
+ }
470
+
471
+ const string &getUsername() const {
472
+ return username;
473
+ }
474
+
475
+ const string &getPassword() const {
476
+ return password;
477
+ }
478
+
479
+ /**
480
+ * @post !result.empty()
481
+ */
482
+ const string &getNodeName() const {
483
+ return nodeName;
484
+ }
485
+ };
486
+
487
+
488
+ inline void
489
+ _checkinConnection(const CorePtr &core, const ConnectionPtr &connection) {
490
+ core->checkinConnection(connection);
491
+ }
492
+
493
+
494
+ } // namespace UnionStation
495
+ } // namespace Passenger
496
+
497
+ #endif /* _PASSENGER_UNION_STATION_CORE_H_ */