passenger 5.0.9 → 5.0.10

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 (106) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +15 -0
  5. data/CONTRIBUTORS +6 -0
  6. data/README.md +1 -1
  7. data/bin/passenger-install-apache2-module +24 -11
  8. data/bin/passenger-status +29 -14
  9. data/build/agents.rb +12 -10
  10. data/build/cxx_tests.rb +30 -30
  11. data/doc/Design and Architecture.html +1 -10
  12. data/doc/Design and Architecture.txt +1 -6
  13. data/doc/Users guide Apache.html +1 -19
  14. data/doc/Users guide Apache.txt +1 -1
  15. data/doc/Users guide Nginx.html +2 -20
  16. data/doc/Users guide Nginx.txt +2 -2
  17. data/doc/users_guide_snippets/tips.txt +0 -9
  18. data/ext/common/ApplicationPool2/ApiKey.h +158 -0
  19. data/ext/common/ApplicationPool2/BasicGroupInfo.h +81 -0
  20. data/ext/common/ApplicationPool2/BasicProcessInfo.h +106 -0
  21. data/ext/common/ApplicationPool2/Common.h +5 -44
  22. data/ext/common/ApplicationPool2/Context.h +94 -0
  23. data/ext/common/ApplicationPool2/Group.h +130 -1205
  24. data/ext/common/ApplicationPool2/Group/InitializationAndShutdown.cpp +190 -0
  25. data/ext/common/ApplicationPool2/Group/InternalUtils.cpp +329 -0
  26. data/ext/common/ApplicationPool2/Group/LifetimeAndBasics.cpp +103 -0
  27. data/ext/common/ApplicationPool2/{Pool/Debug.h → Group/Miscellaneous.cpp} +40 -38
  28. data/ext/common/ApplicationPool2/Group/OutOfBandWork.cpp +323 -0
  29. data/ext/common/ApplicationPool2/Group/ProcessListManagement.cpp +606 -0
  30. data/ext/common/ApplicationPool2/Group/SessionManagement.cpp +337 -0
  31. data/ext/common/ApplicationPool2/Group/SpawningAndRestarting.cpp +478 -0
  32. data/ext/common/ApplicationPool2/Group/StateInspection.cpp +197 -0
  33. data/ext/common/ApplicationPool2/Group/Verification.cpp +159 -0
  34. data/ext/common/ApplicationPool2/Implementation.cpp +19 -1401
  35. data/ext/common/ApplicationPool2/Options.h +5 -5
  36. data/ext/common/ApplicationPool2/Pool.h +260 -815
  37. data/ext/common/ApplicationPool2/Pool/{AnalyticsCollection.h → AnalyticsCollection.cpp} +55 -56
  38. data/ext/common/ApplicationPool2/Pool/{GarbageCollection.h → GarbageCollection.cpp} +49 -49
  39. data/ext/common/ApplicationPool2/Pool/GeneralUtils.cpp +241 -0
  40. data/ext/common/ApplicationPool2/Pool/GroupUtils.cpp +276 -0
  41. data/ext/common/ApplicationPool2/Pool/InitializationAndShutdown.cpp +145 -0
  42. data/ext/common/ApplicationPool2/Pool/Miscellaneous.cpp +244 -0
  43. data/ext/common/ApplicationPool2/Pool/ProcessUtils.cpp +330 -0
  44. data/ext/common/ApplicationPool2/Pool/StateInspection.cpp +299 -0
  45. data/ext/common/ApplicationPool2/Process.h +399 -205
  46. data/ext/common/ApplicationPool2/Session.h +70 -28
  47. data/ext/common/ApplicationPool2/Socket.h +1 -0
  48. data/ext/common/Constants.h +11 -3
  49. data/ext/common/Exceptions.h +1 -1
  50. data/ext/common/Logging.cpp +9 -4
  51. data/ext/common/Logging.h +6 -0
  52. data/ext/common/ServerKit/HttpServer.h +225 -215
  53. data/ext/common/ServerKit/Server.h +57 -57
  54. data/ext/common/SpawningKit/BackgroundIOCapturer.h +160 -0
  55. data/ext/common/SpawningKit/Config.h +107 -0
  56. data/ext/common/{ApplicationPool2 → SpawningKit}/DirectSpawner.h +17 -16
  57. data/ext/common/{ApplicationPool2 → SpawningKit}/DummySpawner.h +33 -33
  58. data/ext/common/{ApplicationPool2/SpawnerFactory.h → SpawningKit/Factory.h} +17 -17
  59. data/ext/common/{ApplicationPool2/ComponentInfo.h → SpawningKit/Options.h} +8 -21
  60. data/ext/common/SpawningKit/PipeWatcher.h +148 -0
  61. data/ext/common/{ApplicationPool2/PipeWatcher.h → SpawningKit/Result.h} +15 -33
  62. data/ext/common/{ApplicationPool2 → SpawningKit}/SmartSpawner.h +52 -57
  63. data/ext/common/{ApplicationPool2 → SpawningKit}/Spawner.h +83 -371
  64. data/ext/common/SpawningKit/UserSwitchingRules.h +265 -0
  65. data/ext/common/Utils/BufferedIO.h +24 -0
  66. data/ext/common/{ApplicationPool2/SpawnObject.h → Utils/ClassUtils.h} +24 -51
  67. data/ext/common/Utils/IOUtils.cpp +70 -0
  68. data/ext/common/Utils/IOUtils.h +19 -0
  69. data/ext/common/Utils/JsonUtils.h +113 -0
  70. data/ext/common/Utils/StrIntUtils.h +29 -0
  71. data/ext/common/Utils/json.h +1 -1
  72. data/ext/common/agents/ApiServerUtils.h +941 -0
  73. data/ext/common/agents/HelperAgent/{AdminServer.h → ApiServer.h} +163 -365
  74. data/ext/common/agents/HelperAgent/Main.cpp +86 -88
  75. data/ext/common/agents/HelperAgent/OptionParser.h +9 -10
  76. data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -0
  77. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +2 -0
  78. data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -1
  79. data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +2 -2
  80. data/ext/common/agents/LoggingAgent/ApiServer.h +279 -0
  81. data/ext/common/agents/LoggingAgent/Main.cpp +41 -51
  82. data/ext/common/agents/LoggingAgent/OptionParser.h +11 -11
  83. data/ext/common/agents/Watchdog/ApiServer.h +311 -0
  84. data/ext/common/agents/Watchdog/Main.cpp +91 -65
  85. data/helper-scripts/prespawn +2 -0
  86. data/lib/phusion_passenger.rb +1 -1
  87. data/lib/phusion_passenger/admin_tools/instance.rb +1 -1
  88. data/lib/phusion_passenger/common_library.rb +27 -14
  89. data/lib/phusion_passenger/config/{admin_command_command.rb → api_call_command.rb} +19 -16
  90. data/lib/phusion_passenger/config/detach_process_command.rb +6 -3
  91. data/lib/phusion_passenger/config/main.rb +3 -5
  92. data/lib/phusion_passenger/config/reopen_logs_command.rb +29 -7
  93. data/lib/phusion_passenger/config/restart_app_command.rb +13 -4
  94. data/lib/phusion_passenger/config/utils.rb +15 -8
  95. data/lib/phusion_passenger/constants.rb +6 -2
  96. data/lib/phusion_passenger/platform_info/apache.rb +4 -0
  97. data/lib/phusion_passenger/platform_info/apache_detector.rb +18 -3
  98. data/resources/templates/apache2/mpm_unknown.txt.erb +20 -0
  99. metadata +42 -21
  100. metadata.gz.asc +7 -7
  101. data/ext/common/ApplicationPool2/Pool/GeneralUtils.h +0 -127
  102. data/ext/common/ApplicationPool2/Pool/Inspection.h +0 -219
  103. data/ext/common/ApplicationPool2/Pool/ProcessUtils.h +0 -85
  104. data/ext/common/ApplicationPool2/SuperGroup.h +0 -706
  105. data/ext/common/agents/LoggingAgent/AdminServer.h +0 -435
  106. data/ext/common/agents/Watchdog/AdminServer.h +0 -432
@@ -0,0 +1,197 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2011-2015 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
+ #include <ApplicationPool2/Group.h>
26
+
27
+ /*************************************************************************
28
+ *
29
+ * Session management functions for ApplicationPool2::Group
30
+ *
31
+ *************************************************************************/
32
+
33
+ namespace Passenger {
34
+ namespace ApplicationPool2 {
35
+
36
+ using namespace std;
37
+ using namespace boost;
38
+
39
+
40
+ /****************************
41
+ *
42
+ * Public methods
43
+ *
44
+ ****************************/
45
+
46
+
47
+ unsigned int
48
+ Group::getProcessCount() const {
49
+ return enabledCount + disablingCount + disabledCount;
50
+ }
51
+
52
+ /**
53
+ * Returns whether the lower bound of the group-specific process limits
54
+ * have been satisfied. Note that even if the result is false, the pool limits
55
+ * may not allow spawning, so you should check `pool->atFullCapacity()` too.
56
+ */
57
+ bool
58
+ Group::processLowerLimitsSatisfied() const {
59
+ return capacityUsed() >= options.minProcesses;
60
+ }
61
+
62
+ /**
63
+ * Returns whether the upper bound of the group-specific process limits have
64
+ * been reached, or surpassed. Does not check whether pool limits have been
65
+ * reached. Use `pool->atFullCapacity()` to check for that.
66
+ */
67
+ bool
68
+ Group::processUpperLimitsReached() const {
69
+ return options.maxProcesses != 0 && capacityUsed() >= options.maxProcesses;
70
+ }
71
+
72
+ /**
73
+ * Returns whether all enabled processes are totally busy. If so, another
74
+ * process should be spawned, if allowed by the process limits.
75
+ * Returns false if there are no enabled processes.
76
+ */
77
+ bool
78
+ Group::allEnabledProcessesAreTotallyBusy() const {
79
+ return nEnabledProcessesTotallyBusy == enabledCount;
80
+ }
81
+
82
+ /**
83
+ * Returns the number of processes in this group that should be part of the
84
+ * ApplicationPool process limits calculations.
85
+ */
86
+ unsigned int
87
+ Group::capacityUsed() const {
88
+ return enabledCount + disablingCount + disabledCount + processesBeingSpawned;
89
+ }
90
+
91
+ /**
92
+ * Checks whether this group is waiting for capacity on the pool to
93
+ * become available before it can continue processing requests.
94
+ */
95
+ bool
96
+ Group::isWaitingForCapacity() const {
97
+ return enabledProcesses.empty()
98
+ && processesBeingSpawned == 0
99
+ && !m_restarting
100
+ && !getWaitlist.empty();
101
+ }
102
+
103
+ bool
104
+ Group::garbageCollectable(unsigned long long now) const {
105
+ /* if (now == 0) {
106
+ now = SystemTime::getUsec();
107
+ }
108
+ return busyness() == 0
109
+ && getWaitlist.empty()
110
+ && disabledProcesses.empty()
111
+ && options.getMaxPreloaderIdleTime() != 0
112
+ && now - spawner->lastUsed() >
113
+ (unsigned long long) options.getMaxPreloaderIdleTime() * 1000000; */
114
+ return false;
115
+ }
116
+
117
+ void
118
+ Group::inspectXml(std::ostream &stream, bool includeSecrets) const {
119
+ ProcessList::const_iterator it;
120
+
121
+ stream << "<name>" << escapeForXml(info.name) << "</name>";
122
+ stream << "<component_name>" << escapeForXml(info.name) << "</component_name>";
123
+ stream << "<app_root>" << escapeForXml(options.appRoot) << "</app_root>";
124
+ stream << "<app_type>" << escapeForXml(options.appType) << "</app_type>";
125
+ stream << "<environment>" << escapeForXml(options.environment) << "</environment>";
126
+ stream << "<uuid>" << toString(uuid) << "</uuid>";
127
+ stream << "<enabled_process_count>" << enabledCount << "</enabled_process_count>";
128
+ stream << "<disabling_process_count>" << disablingCount << "</disabling_process_count>";
129
+ stream << "<disabled_process_count>" << disabledCount << "</disabled_process_count>";
130
+ stream << "<capacity_used>" << capacityUsed() << "</capacity_used>";
131
+ stream << "<get_wait_list_size>" << getWaitlist.size() << "</get_wait_list_size>";
132
+ stream << "<disable_wait_list_size>" << disableWaitlist.size() << "</disable_wait_list_size>";
133
+ stream << "<processes_being_spawned>" << processesBeingSpawned << "</processes_being_spawned>";
134
+ if (m_spawning) {
135
+ stream << "<spawning/>";
136
+ }
137
+ if (restarting()) {
138
+ stream << "<restarting/>";
139
+ }
140
+ if (includeSecrets) {
141
+ stream << "<secret>" << escapeForXml(getApiKey().toStaticString()) << "</secret>";
142
+ stream << "<api_key>" << escapeForXml(getApiKey().toStaticString()) << "</api_key>";
143
+ }
144
+ LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
145
+ switch (lifeStatus) {
146
+ case ALIVE:
147
+ stream << "<life_status>ALIVE</life_status>";
148
+ break;
149
+ case SHUTTING_DOWN:
150
+ stream << "<life_status>SHUTTING_DOWN</life_status>";
151
+ break;
152
+ case SHUT_DOWN:
153
+ stream << "<life_status>SHUT_DOWN</life_status>";
154
+ break;
155
+ default:
156
+ P_BUG("Unknown 'lifeStatus' state " << lifeStatus);
157
+ }
158
+
159
+ SpawningKit::UserSwitchingInfo usInfo(SpawningKit::prepareUserSwitching(options));
160
+ stream << "<user>" << escapeForXml(usInfo.username) << "</user>";
161
+ stream << "<uid>" << usInfo.uid << "</uid>";
162
+ stream << "<group>" << escapeForXml(usInfo.groupname) << "</group>";
163
+ stream << "<gid>" << usInfo.gid << "</gid>";
164
+
165
+ stream << "<options>";
166
+ options.toXml(stream, getResourceLocator());
167
+ stream << "</options>";
168
+
169
+ stream << "<processes>";
170
+
171
+ for (it = enabledProcesses.begin(); it != enabledProcesses.end(); it++) {
172
+ stream << "<process>";
173
+ (*it)->inspectXml(stream, includeSecrets);
174
+ stream << "</process>";
175
+ }
176
+ for (it = disablingProcesses.begin(); it != disablingProcesses.end(); it++) {
177
+ stream << "<process>";
178
+ (*it)->inspectXml(stream, includeSecrets);
179
+ stream << "</process>";
180
+ }
181
+ for (it = disabledProcesses.begin(); it != disabledProcesses.end(); it++) {
182
+ stream << "<process>";
183
+ (*it)->inspectXml(stream, includeSecrets);
184
+ stream << "</process>";
185
+ }
186
+ for (it = detachedProcesses.begin(); it != detachedProcesses.end(); it++) {
187
+ stream << "<process>";
188
+ (*it)->inspectXml(stream, includeSecrets);
189
+ stream << "</process>";
190
+ }
191
+
192
+ stream << "</processes>";
193
+ }
194
+
195
+
196
+ } // namespace ApplicationPool2
197
+ } // namespace Passenger
@@ -0,0 +1,159 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2011-2015 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
+ #include <ApplicationPool2/Group.h>
26
+
27
+ /*************************************************************************
28
+ *
29
+ * Correctness verification functions for ApplicationPool2::Group
30
+ *
31
+ *************************************************************************/
32
+
33
+ namespace Passenger {
34
+ namespace ApplicationPool2 {
35
+
36
+ using namespace std;
37
+ using namespace boost;
38
+
39
+
40
+ /****************************
41
+ *
42
+ * Private methods
43
+ *
44
+ ****************************/
45
+
46
+
47
+ bool
48
+ Group::selfCheckingEnabled() const {
49
+ return pool->selfchecking;
50
+ }
51
+
52
+ void
53
+ Group::verifyInvariants() const {
54
+ // !a || b: logical equivalent of a IMPLIES b.
55
+ #ifndef NDEBUG
56
+ if (!selfCheckingEnabled()) {
57
+ return;
58
+ }
59
+
60
+ LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
61
+
62
+ assert(enabledCount >= 0);
63
+ assert(disablingCount >= 0);
64
+ assert(disabledCount >= 0);
65
+ assert(nEnabledProcessesTotallyBusy >= 0);
66
+ assert(!( enabledCount == 0 && disablingCount > 0 ) || ( processesBeingSpawned > 0) );
67
+ assert(!( !m_spawning ) || ( enabledCount > 0 || disablingCount == 0 ));
68
+
69
+ assert((lifeStatus == ALIVE) == (spawner != NULL));
70
+
71
+ // Verify getWaitlist invariants.
72
+ assert(!( !getWaitlist.empty() ) || ( enabledProcesses.empty() || verifyNoRequestsOnGetWaitlistAreRoutable() ));
73
+ assert(!( enabledProcesses.empty() && !m_spawning && !restarting() && !poolAtFullCapacity() ) || ( getWaitlist.empty() ));
74
+ assert(!( !getWaitlist.empty() ) || ( !enabledProcesses.empty() || m_spawning || restarting() || poolAtFullCapacity() ));
75
+
76
+ // Verify disableWaitlist invariants.
77
+ assert((int) disableWaitlist.size() >= disablingCount);
78
+
79
+ // Verify processesBeingSpawned, m_spawning and m_restarting.
80
+ assert(!( processesBeingSpawned > 0 ) || ( m_spawning ));
81
+ assert(!( m_restarting ) || ( processesBeingSpawned == 0 ));
82
+
83
+ // Verify lifeStatus.
84
+ if (lifeStatus != ALIVE) {
85
+ assert(enabledCount == 0);
86
+ assert(disablingCount == 0);
87
+ assert(disabledCount == 0);
88
+ assert(nEnabledProcessesTotallyBusy == 0);
89
+ }
90
+
91
+ // Verify list sizes.
92
+ assert((int) enabledProcesses.size() == enabledCount);
93
+ assert((int) disablingProcesses.size() == disablingCount);
94
+ assert((int) disabledProcesses.size() == disabledCount);
95
+ assert(nEnabledProcessesTotallyBusy <= enabledCount);
96
+ #endif
97
+ }
98
+
99
+ void
100
+ Group::verifyExpensiveInvariants() const {
101
+ #ifndef NDEBUG
102
+ // !a || b: logical equivalent of a IMPLIES b.
103
+
104
+ if (!selfCheckingEnabled()) {
105
+ return;
106
+ }
107
+
108
+ ProcessList::const_iterator it, end;
109
+
110
+ end = enabledProcesses.end();
111
+ for (it = enabledProcesses.begin(); it != end; it++) {
112
+ const ProcessPtr &process = *it;
113
+ assert(process->enabled == Process::ENABLED);
114
+ assert(process->isAlive());
115
+ assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
116
+ || process->oobwStatus == Process::OOBW_REQUESTED);
117
+ }
118
+
119
+ end = disablingProcesses.end();
120
+ for (it = disablingProcesses.begin(); it != end; it++) {
121
+ const ProcessPtr &process = *it;
122
+ assert(process->enabled == Process::DISABLING);
123
+ assert(process->isAlive());
124
+ assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
125
+ || process->oobwStatus == Process::OOBW_IN_PROGRESS);
126
+ }
127
+
128
+ end = disabledProcesses.end();
129
+ for (it = disabledProcesses.begin(); it != end; it++) {
130
+ const ProcessPtr &process = *it;
131
+ assert(process->enabled == Process::DISABLED);
132
+ assert(process->isAlive());
133
+ assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
134
+ || process->oobwStatus == Process::OOBW_IN_PROGRESS);
135
+ }
136
+
137
+ foreach (const ProcessPtr &process, detachedProcesses) {
138
+ assert(process->enabled == Process::DETACHED);
139
+ }
140
+ #endif
141
+ }
142
+
143
+ #ifndef NDEBUG
144
+ bool
145
+ Group::verifyNoRequestsOnGetWaitlistAreRoutable() const {
146
+ deque<GetWaiter>::const_iterator it, end = getWaitlist.end();
147
+
148
+ for (it = getWaitlist.begin(); it != end; it++) {
149
+ if (route(it->options).process != NULL) {
150
+ return false;
151
+ }
152
+ }
153
+ return true;
154
+ }
155
+ #endif
156
+
157
+
158
+ } // namespace ApplicationPool2
159
+ } // namespace Passenger
@@ -35,10 +35,26 @@
35
35
  #include <boost/date_time/posix_time/posix_time_types.hpp>
36
36
  #include <oxt/backtrace.hpp>
37
37
  #include <ApplicationPool2/Pool.h>
38
- #include <ApplicationPool2/SuperGroup.h>
39
38
  #include <ApplicationPool2/Group.h>
40
- #include <ApplicationPool2/PipeWatcher.h>
41
39
  #include <ApplicationPool2/ErrorRenderer.h>
40
+ #include <ApplicationPool2/Pool/InitializationAndShutdown.cpp>
41
+ #include <ApplicationPool2/Pool/AnalyticsCollection.cpp>
42
+ #include <ApplicationPool2/Pool/GarbageCollection.cpp>
43
+ #include <ApplicationPool2/Pool/GeneralUtils.cpp>
44
+ #include <ApplicationPool2/Pool/GroupUtils.cpp>
45
+ #include <ApplicationPool2/Pool/ProcessUtils.cpp>
46
+ #include <ApplicationPool2/Pool/StateInspection.cpp>
47
+ #include <ApplicationPool2/Pool/Miscellaneous.cpp>
48
+ #include <ApplicationPool2/Group/InitializationAndShutdown.cpp>
49
+ #include <ApplicationPool2/Group/LifetimeAndBasics.cpp>
50
+ #include <ApplicationPool2/Group/SessionManagement.cpp>
51
+ #include <ApplicationPool2/Group/SpawningAndRestarting.cpp>
52
+ #include <ApplicationPool2/Group/ProcessListManagement.cpp>
53
+ #include <ApplicationPool2/Group/OutOfBandWork.cpp>
54
+ #include <ApplicationPool2/Group/Miscellaneous.cpp>
55
+ #include <ApplicationPool2/Group/InternalUtils.cpp>
56
+ #include <ApplicationPool2/Group/StateInspection.cpp>
57
+ #include <ApplicationPool2/Group/Verification.cpp>
42
58
  #include <Exceptions.h>
43
59
  #include <Hooks.h>
44
60
  #include <MessageReadersWriters.h>
@@ -148,7 +164,7 @@ rethrowException(const ExceptionPtr &e) {
148
164
  }
149
165
 
150
166
  void processAndLogNewSpawnException(SpawnException &e, const Options &options,
151
- const SpawnerConfigPtr &config)
167
+ const SpawningKit::ConfigPtr &config)
152
168
  {
153
169
  TRACE_POINT();
154
170
  UnionStation::TransactionPtr transaction;
@@ -307,1303 +323,6 @@ recreateString(psg_pool_t *pool, StaticString &str) {
307
323
  }
308
324
 
309
325
 
310
- const SuperGroupPtr
311
- Pool::getSuperGroup(const char *name) {
312
- SuperGroupPtr *superGroup;
313
- if (superGroups.lookup(name, &superGroup)) {
314
- return *superGroup;
315
- } else {
316
- return SuperGroupPtr();
317
- }
318
- }
319
-
320
-
321
- boost::mutex &
322
- SuperGroup::getPoolSyncher(Pool *pool) {
323
- return pool->syncher;
324
- }
325
-
326
- void
327
- SuperGroup::runAllActions(const boost::container::vector<Callback> &actions) {
328
- Pool::runAllActions(actions);
329
- }
330
-
331
- const PoolPtr
332
- SuperGroup::getPoolPtr() {
333
- return getPool()->shared_from_this();
334
- }
335
-
336
- string
337
- SuperGroup::generateSecret() const {
338
- return getPool()->getRandomGenerator()->generateAsciiString(43);
339
- }
340
-
341
- bool
342
- SuperGroup::selfCheckingEnabled() const {
343
- return getPool()->selfchecking;
344
- }
345
-
346
- void
347
- SuperGroup::runInitializationHooks() const {
348
- getPool()->runHookScripts("after_initialize_supergroup",
349
- boost::bind(&SuperGroup::setupInitializationOrDestructionHook, this, _1));
350
- }
351
-
352
- void
353
- SuperGroup::runDestructionHooks() const {
354
- getPool()->runHookScripts("before_destroy_supergroup",
355
- boost::bind(&SuperGroup::setupInitializationOrDestructionHook, this, _1));
356
- }
357
-
358
- void
359
- SuperGroup::setupInitializationOrDestructionHook(HookScriptOptions &options) const {
360
- options.environment.push_back(make_pair("PASSENGER_APP_ROOT", this->options.appRoot));
361
- }
362
-
363
- void
364
- SuperGroup::createInterruptableThread(const boost::function<void ()> &func, const string &name,
365
- unsigned int stackSize)
366
- {
367
- getPool()->interruptableThreads.create_thread(func, name, stackSize);
368
- }
369
-
370
- void
371
- SuperGroup::realDoInitialize(const Options &options, unsigned int generation) {
372
- vector<ComponentInfo> componentInfos;
373
- vector<ComponentInfo>::const_iterator it;
374
- ExceptionPtr exception;
375
-
376
- Pool *pool = getPool();
377
-
378
- P_TRACE(2, "Initializing SuperGroup " << inspect() << " in the background...");
379
- try {
380
- componentInfos = loadComponentInfos(options);
381
- } catch (const tracable_exception &e) {
382
- exception = copyException(e);
383
- }
384
- if (componentInfos.empty() && exception == NULL) {
385
- string message = "The directory " +
386
- options.appRoot +
387
- " does not seem to contain a web application.";
388
- boost::shared_ptr<SpawnException> spawnException =
389
- boost::make_shared<SpawnException>(
390
- message, message, false);
391
- exception = spawnException;
392
- processAndLogNewSpawnException(*spawnException, options,
393
- pool->getSpawnerConfig());
394
- }
395
-
396
- Pool::DebugSupportPtr debug = pool->debugSupport;
397
- boost::container::vector<Callback> actions;
398
- {
399
- if (debug != NULL && debug->superGroup) {
400
- debug->debugger->send("About to finish SuperGroup initialization");
401
- debug->messages->recv("Proceed with initializing SuperGroup");
402
- }
403
-
404
- boost::unique_lock<boost::mutex> lock(getPoolSyncher(pool));
405
- this_thread::disable_interruption di;
406
- this_thread::disable_syscall_interruption dsi;
407
- NOT_EXPECTING_EXCEPTIONS();
408
- if (OXT_UNLIKELY(getPool() == NULL || generation != this->generation)) {
409
- return;
410
- }
411
- P_TRACE(2, "Initialization of SuperGroup " << inspect() << " almost done; grabbed lock");
412
- assert(state == INITIALIZING);
413
- verifyInvariants();
414
-
415
- if (componentInfos.empty()) {
416
- /* Somehow initialization failed. Maybe something has deleted
417
- * the supergroup files while we're working.
418
- */
419
- assert(exception != NULL);
420
- setState(DESTROYED);
421
-
422
- actions.reserve(getWaitlist.size());
423
- while (!getWaitlist.empty()) {
424
- const GetWaiter &waiter = getWaitlist.front();
425
- actions.push_back(boost::bind(GetCallback::call,
426
- waiter.callback, SessionPtr(), exception));
427
- getWaitlist.pop_front();
428
- }
429
- } else {
430
- for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
431
- const ComponentInfo &info = *it;
432
- GroupPtr group = boost::make_shared<Group>(this,
433
- options, info);
434
- group->initialize();
435
- groups.push_back(group);
436
- if (info.isDefault) {
437
- defaultGroup = group.get();
438
- }
439
- }
440
-
441
- setState(READY);
442
- assignGetWaitlistToGroups(actions);
443
- }
444
-
445
- verifyInvariants();
446
- P_TRACE(2, "Done initializing SuperGroup " << inspect());
447
- }
448
-
449
- this_thread::disable_interruption di;
450
- this_thread::disable_syscall_interruption dsi;
451
- runAllActions(actions);
452
- runInitializationHooks();
453
- }
454
-
455
- void
456
- SuperGroup::realDoRestart(const Options &options, unsigned int generation) {
457
- TRACE_POINT();
458
- vector<ComponentInfo> componentInfos = loadComponentInfos(options);
459
- vector<ComponentInfo>::const_iterator it;
460
-
461
- Pool *pool = getPool();
462
- Pool::DebugSupportPtr debug = pool->debugSupport;
463
- if (debug != NULL && debug->superGroup) {
464
- debug->debugger->send("About to finish SuperGroup restart");
465
- debug->messages->recv("Proceed with restarting SuperGroup");
466
- }
467
-
468
- boost::unique_lock<boost::mutex> lock(getPoolSyncher(pool));
469
- if (OXT_UNLIKELY(this->generation != generation)) {
470
- return;
471
- }
472
-
473
- assert(state == RESTARTING);
474
- verifyInvariants();
475
-
476
- SuperGroup::GroupList allGroups;
477
- SuperGroup::GroupList updatedGroups;
478
- SuperGroup::GroupList newGroups;
479
- SuperGroup::GroupList::const_iterator g_it;
480
- boost::container::vector<Callback> actions;
481
- this->options = options;
482
-
483
- // Update the component information for existing groups.
484
- UPDATE_TRACE_POINT();
485
- for (it = componentInfos.begin(); it != componentInfos.end(); it++) {
486
- const ComponentInfo &info = *it;
487
- pair<GroupPtr, unsigned int> result =
488
- findGroupCorrespondingToComponent(groups, info);
489
- GroupPtr group = result.first;
490
- if (group != NULL) {
491
- unsigned int index = result.second;
492
- group->componentInfo = info;
493
- updatedGroups.push_back(group);
494
- groups[index].reset();
495
- } else {
496
- // This is not an existing group but a new one,
497
- // so create it.
498
- group = boost::make_shared<Group>(this,
499
- options, info);
500
- group->initialize();
501
- newGroups.push_back(group);
502
- }
503
- // allGroups must be in the same order as componentInfos.
504
- allGroups.push_back(group);
505
- }
506
-
507
- // Some components might have been deleted, so delete the
508
- // corresponding groups.
509
- detachAllGroups(groups, actions);
510
-
511
- // Tell all previous existing groups to restart.
512
- for (g_it = updatedGroups.begin(); g_it != updatedGroups.end(); g_it++) {
513
- GroupPtr group = *g_it;
514
- group->restart(options);
515
- }
516
-
517
- groups = allGroups;
518
- defaultGroup = findDefaultGroup(allGroups);
519
- setState(READY);
520
- assignGetWaitlistToGroups(actions);
521
-
522
- UPDATE_TRACE_POINT();
523
- verifyInvariants();
524
- lock.unlock();
525
- runAllActions(actions);
526
- }
527
-
528
-
529
- Group::Group(SuperGroup *_superGroup, const Options &_options, const ComponentInfo &info)
530
- : superGroup(_superGroup),
531
- name(_superGroup->name + "#" + info.name),
532
- uuid(generateUuid(_superGroup)),
533
- componentInfo(info)
534
- {
535
- generateSecret(_superGroup, secret);
536
- resetOptions(_options);
537
- enabledCount = 0;
538
- disablingCount = 0;
539
- disabledCount = 0;
540
- nEnabledProcessesTotallyBusy = 0;
541
- spawner = getPool()->spawnerFactory->create(options);
542
- restartsInitiated = 0;
543
- processesBeingSpawned = 0;
544
- m_spawning = false;
545
- m_restarting = false;
546
- lifeStatus.store(ALIVE, boost::memory_order_relaxed);
547
- lastRestartFileMtime = 0;
548
- lastRestartFileCheckTime = 0;
549
- alwaysRestartFileExists = false;
550
- if (options.restartDir.empty()) {
551
- restartFile = options.appRoot + "/tmp/restart.txt";
552
- alwaysRestartFile = options.appRoot + "/tmp/always_restart.txt";
553
- } else if (options.restartDir[0] == '/') {
554
- restartFile = options.restartDir + "/restart.txt";
555
- alwaysRestartFile = options.restartDir + "/always_restart.txt";
556
- } else {
557
- restartFile = options.appRoot + "/" + options.restartDir + "/restart.txt";
558
- alwaysRestartFile = options.appRoot + "/" + options.restartDir + "/always_restart.txt";
559
- }
560
-
561
- detachedProcessesCheckerActive = false;
562
- }
563
-
564
- Group::~Group() {
565
- LifeStatus lifeStatus = getLifeStatus();
566
- if (OXT_UNLIKELY(lifeStatus == ALIVE)) {
567
- P_BUG("You must call Group::shutdown() before destroying a Group.");
568
- }
569
- assert(lifeStatus == SHUT_DOWN);
570
- assert(!detachedProcessesCheckerActive);
571
- assert(getWaitlist.empty());
572
- }
573
-
574
- void
575
- Group::initialize() {
576
- nullProcess = boost::make_shared<Process>(
577
- 0, StaticString(),
578
- FileDescriptor(), FileDescriptor(),
579
- SocketList(), 0, 0);
580
- nullProcess->dummy = true;
581
- nullProcess->requiresShutdown = false;
582
- nullProcess->setGroup(this);
583
- }
584
-
585
- OXT_FORCE_INLINE Pool *
586
- Group::getPool() const {
587
- return getSuperGroup()->getPool();
588
- }
589
-
590
- void
591
- Group::_onSessionInitiateFailure(Session *session) {
592
- Process *process = session->getProcess();
593
- assert(process != NULL);
594
- process->getGroup()->onSessionInitiateFailure(process, session);
595
- }
596
-
597
- void
598
- Group::_onSessionClose(Session *session) {
599
- Process *process = session->getProcess();
600
- assert(process != NULL);
601
- process->getGroup()->onSessionClose(process, session);
602
- }
603
-
604
- OXT_FORCE_INLINE void
605
- Group::onSessionInitiateFailure(Process *process, Session *session) {
606
- boost::container::vector<Callback> actions;
607
-
608
- TRACE_POINT();
609
- // Standard resource management boilerplate stuff...
610
- Pool *pool = getPool();
611
- boost::unique_lock<boost::mutex> lock(pool->syncher);
612
- assert(process->isAlive());
613
- assert(isAlive() || getLifeStatus() == SHUTTING_DOWN);
614
-
615
- UPDATE_TRACE_POINT();
616
- P_DEBUG("Could not initiate a session with process " <<
617
- process->inspect() << ", detaching from pool if possible");
618
- if (!pool->detachProcessUnlocked(process->shared_from_this(), actions)) {
619
- P_DEBUG("Process was already detached");
620
- }
621
- pool->fullVerifyInvariants();
622
- lock.unlock();
623
- runAllActions(actions);
624
- }
625
-
626
- OXT_FORCE_INLINE void
627
- Group::onSessionClose(Process *process, Session *session) {
628
- TRACE_POINT();
629
- // Standard resource management boilerplate stuff...
630
- Pool *pool = getPool();
631
- boost::unique_lock<boost::mutex> lock(pool->syncher);
632
- assert(process->isAlive());
633
- assert(isAlive() || getLifeStatus() == SHUTTING_DOWN);
634
-
635
- P_TRACE(2, "Session closed for process " << process->inspect());
636
- verifyInvariants();
637
- UPDATE_TRACE_POINT();
638
-
639
- /* Update statistics. */
640
- bool wasTotallyBusy = process->isTotallyBusy();
641
- process->sessionClosed(session);
642
- assert(process->getLifeStatus() == Process::ALIVE);
643
- assert(process->enabled == Process::ENABLED
644
- || process->enabled == Process::DISABLING
645
- || process->enabled == Process::DETACHED);
646
- if (process->enabled == Process::ENABLED) {
647
- enabledProcessBusynessLevels[process->index] = process->busyness();
648
- if (wasTotallyBusy) {
649
- assert(nEnabledProcessesTotallyBusy >= 1);
650
- nEnabledProcessesTotallyBusy--;
651
- }
652
- }
653
-
654
- /* This group now has a process that's guaranteed to be not
655
- * totally busy.
656
- */
657
- assert(!process->isTotallyBusy());
658
-
659
- bool detachingBecauseOfMaxRequests = false;
660
- bool detachingBecauseCapacityNeeded = false;
661
- bool shouldDetach =
662
- ( detachingBecauseOfMaxRequests = (
663
- options.maxRequests > 0
664
- && process->processed >= options.maxRequests
665
- )) || (
666
- detachingBecauseCapacityNeeded = (
667
- process->sessions == 0
668
- && getWaitlist.empty()
669
- && (
670
- !pool->getWaitlist.empty()
671
- || anotherGroupIsWaitingForCapacity()
672
- )
673
- )
674
- );
675
- bool shouldDisable =
676
- process->enabled == Process::DISABLING
677
- && process->sessions == 0
678
- && enabledCount > 0;
679
-
680
- if (shouldDetach || shouldDisable) {
681
- UPDATE_TRACE_POINT();
682
- boost::container::vector<Callback> actions;
683
-
684
- if (shouldDetach) {
685
- if (detachingBecauseCapacityNeeded) {
686
- /* Someone might be trying to get() a session for a different
687
- * group that couldn't be spawned because of lack of pool capacity.
688
- * If this group isn't under sufficiently load (as apparent by the
689
- * checked conditions) then now's a good time to detach
690
- * this process or group in order to free capacity.
691
- */
692
- P_DEBUG("Process " << process->inspect() << " is no longer totally "
693
- "busy; detaching it in order to make room in the pool");
694
- } else {
695
- /* This process has processed its maximum number of requests,
696
- * so we detach it.
697
- */
698
- P_DEBUG("Process " << process->inspect() <<
699
- " has reached its maximum number of requests (" <<
700
- options.maxRequests << "); detaching it");
701
- }
702
- pool->detachProcessUnlocked(process->shared_from_this(), actions);
703
- } else {
704
- ProcessPtr processPtr = process->shared_from_this();
705
- removeProcessFromList(processPtr, disablingProcesses);
706
- addProcessToList(processPtr, disabledProcesses);
707
- removeFromDisableWaitlist(processPtr, DR_SUCCESS, actions);
708
- maybeInitiateOobw(process);
709
- }
710
-
711
- pool->fullVerifyInvariants();
712
- lock.unlock();
713
- runAllActions(actions);
714
-
715
- } else {
716
- UPDATE_TRACE_POINT();
717
-
718
- // This could change process->enabled.
719
- maybeInitiateOobw(process);
720
-
721
- if (!getWaitlist.empty() && process->enabled == Process::ENABLED) {
722
- /* If there are clients on this group waiting for a process to
723
- * become available then call them now.
724
- */
725
- UPDATE_TRACE_POINT();
726
- // Already calls verifyInvariants().
727
- assignSessionsToGetWaitersQuickly(lock);
728
- }
729
- }
730
- }
731
-
732
- void
733
- Group::requestOOBW(const ProcessPtr &process) {
734
- // Standard resource management boilerplate stuff...
735
- Pool *pool = getPool();
736
- boost::unique_lock<boost::mutex> lock(pool->syncher);
737
- if (isAlive() && process->isAlive() && process->oobwStatus == Process::OOBW_NOT_ACTIVE) {
738
- process->oobwStatus = Process::OOBW_REQUESTED;
739
- }
740
- }
741
-
742
- bool
743
- Group::oobwAllowed() const {
744
- unsigned int oobwInstances = 0;
745
- foreach (const ProcessPtr &process, disablingProcesses) {
746
- if (process->oobwStatus == Process::OOBW_IN_PROGRESS) {
747
- oobwInstances += 1;
748
- }
749
- }
750
- foreach (const ProcessPtr &process, disabledProcesses) {
751
- if (process->oobwStatus == Process::OOBW_IN_PROGRESS) {
752
- oobwInstances += 1;
753
- }
754
- }
755
- return oobwInstances < options.maxOutOfBandWorkInstances;
756
- }
757
-
758
- bool
759
- Group::shouldInitiateOobw(Process *process) const {
760
- return process->oobwStatus == Process::OOBW_REQUESTED
761
- && process->enabled != Process::DETACHED
762
- && process->isAlive()
763
- && oobwAllowed();
764
- }
765
-
766
- void
767
- Group::maybeInitiateOobw(Process *process) {
768
- if (shouldInitiateOobw(process)) {
769
- // We keep an extra reference to prevent premature destruction.
770
- ProcessPtr p = process->shared_from_this();
771
- initiateOobw(p);
772
- }
773
- }
774
-
775
- // The 'self' parameter is for keeping the current Group object alive
776
- void
777
- Group::lockAndMaybeInitiateOobw(const ProcessPtr &process, DisableResult result, GroupPtr self) {
778
- TRACE_POINT();
779
-
780
- // Standard resource management boilerplate stuff...
781
- Pool *pool = getPool();
782
- boost::unique_lock<boost::mutex> lock(pool->syncher);
783
- if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
784
- return;
785
- }
786
-
787
- assert(process->oobwStatus == Process::OOBW_IN_PROGRESS);
788
-
789
- if (result == DR_SUCCESS) {
790
- if (process->enabled == Process::DISABLED) {
791
- P_DEBUG("Process " << process->inspect() << " disabled; proceeding " <<
792
- "with out-of-band work");
793
- process->oobwStatus = Process::OOBW_REQUESTED;
794
- if (shouldInitiateOobw(process.get())) {
795
- initiateOobw(process);
796
- } else {
797
- // We do not re-enable the process because it's likely that the
798
- // administrator has explicitly changed the state.
799
- P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
800
- "because the process no longer requests out-of-band work");
801
- process->oobwStatus = Process::OOBW_NOT_ACTIVE;
802
- }
803
- } else {
804
- // We do not re-enable the process because it's likely that the
805
- // administrator has explicitly changed the state.
806
- P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
807
- "because the process was reenabled after disabling");
808
- process->oobwStatus = Process::OOBW_NOT_ACTIVE;
809
- }
810
- } else {
811
- P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
812
- "because the process could not be disabled");
813
- process->oobwStatus = Process::OOBW_NOT_ACTIVE;
814
- }
815
- }
816
-
817
- void
818
- Group::initiateOobw(const ProcessPtr &process) {
819
- assert(process->oobwStatus == Process::OOBW_REQUESTED);
820
-
821
- process->oobwStatus = Process::OOBW_IN_PROGRESS;
822
-
823
- if (process->enabled == Process::ENABLED
824
- || process->enabled == Process::DISABLING)
825
- {
826
- // We want the process to be disabled. However, disabling a process is potentially
827
- // asynchronous, so we pass a callback which will re-aquire the lock and call this
828
- // method again.
829
- P_DEBUG("Disabling process " << process->inspect() << " in preparation for OOBW");
830
- DisableResult result = disable(process,
831
- boost::bind(&Group::lockAndMaybeInitiateOobw, this,
832
- _1, _2, shared_from_this()));
833
- switch (result) {
834
- case DR_SUCCESS:
835
- // Continue code flow.
836
- break;
837
- case DR_DEFERRED:
838
- // lockAndMaybeInitiateOobw() will eventually be called.
839
- return;
840
- case DR_ERROR:
841
- case DR_NOOP:
842
- P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted "
843
- "because the process could not be disabled");
844
- process->oobwStatus = Process::OOBW_NOT_ACTIVE;
845
- return;
846
- default:
847
- P_BUG("Unexpected disable() result " << result);
848
- }
849
- }
850
-
851
- assert(process->enabled == Process::DISABLED);
852
- assert(process->sessions == 0);
853
-
854
- P_DEBUG("Initiating OOBW request for process " << process->inspect());
855
- interruptableThreads.create_thread(
856
- boost::bind(&Group::spawnThreadOOBWRequest, this, shared_from_this(), process),
857
- "OOBW request thread for process " + process->inspect(),
858
- POOL_HELPER_THREAD_STACK_SIZE);
859
- }
860
-
861
- // The 'self' parameter is for keeping the current Group object alive while this thread is running.
862
- void
863
- Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) {
864
- TRACE_POINT();
865
- this_thread::disable_interruption di;
866
- this_thread::disable_syscall_interruption dsi;
867
-
868
- Socket *socket;
869
- Connection connection;
870
- Pool *pool = getPool();
871
- Pool::DebugSupportPtr debug = pool->debugSupport;
872
-
873
- UPDATE_TRACE_POINT();
874
- P_DEBUG("Performing OOBW request for process " << process->inspect());
875
- if (debug != NULL && debug->oobw) {
876
- debug->debugger->send("OOBW request about to start");
877
- debug->messages->recv("Proceed with OOBW request");
878
- }
879
-
880
- UPDATE_TRACE_POINT();
881
- {
882
- // Standard resource management boilerplate stuff...
883
- boost::unique_lock<boost::mutex> lock(pool->syncher);
884
- if (OXT_UNLIKELY(!process->isAlive()
885
- || process->enabled == Process::DETACHED
886
- || !isAlive()))
887
- {
888
- return;
889
- }
890
-
891
- if (process->enabled != Process::DISABLED) {
892
- UPDATE_TRACE_POINT();
893
- P_INFO("Out-of-Band Work canceled: process " << process->inspect() <<
894
- " was concurrently re-enabled.");
895
- if (debug != NULL && debug->oobw) {
896
- debug->debugger->send("OOBW request canceled");
897
- }
898
- return;
899
- }
900
-
901
- assert(process->oobwStatus == Process::OOBW_IN_PROGRESS);
902
- assert(process->sessions == 0);
903
- socket = process->findSessionSocketWithLowestBusyness();
904
- }
905
-
906
- UPDATE_TRACE_POINT();
907
- unsigned long long timeout = 1000 * 1000 * 60; // 1 min
908
- try {
909
- this_thread::restore_interruption ri(di);
910
- this_thread::restore_syscall_interruption rsi(dsi);
911
-
912
- // Grab a connection. The connection is marked as fail in order to
913
- // ensure it is closed / recycled after this request (otherwise we'd
914
- // need to completely read the response).
915
- connection = socket->checkoutConnection();
916
- connection.fail = true;
917
- ScopeGuard guard(boost::bind(&Socket::checkinConnection, socket, connection));
918
-
919
- // This is copied from RequestHandler when it is sending data using the
920
- // "session" protocol.
921
- char sizeField[sizeof(boost::uint32_t)];
922
- SmallVector<StaticString, 10> data;
923
-
924
- data.push_back(StaticString(sizeField, sizeof(boost::uint32_t)));
925
- data.push_back(P_STATIC_STRING_WITH_NULL("REQUEST_METHOD"));
926
- data.push_back(P_STATIC_STRING_WITH_NULL("OOBW"));
927
-
928
- data.push_back(P_STATIC_STRING_WITH_NULL("PASSENGER_CONNECT_PASSWORD"));
929
- data.push_back(StaticString(secret, SECRET_SIZE));
930
- data.push_back(StaticString("", 1));
931
-
932
- boost::uint32_t dataSize = 0;
933
- for (unsigned int i = 1; i < data.size(); i++) {
934
- dataSize += (boost::uint32_t) data[i].size();
935
- }
936
- Uint32Message::generate(sizeField, dataSize);
937
-
938
- gatheredWrite(connection.fd, &data[0], data.size(), &timeout);
939
-
940
- // We do not care what the actual response is ... just wait for it.
941
- UPDATE_TRACE_POINT();
942
- waitUntilReadable(connection.fd, &timeout);
943
- } catch (const SystemException &e) {
944
- P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace());
945
- } catch (const TimeoutException &e) {
946
- P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace());
947
- }
948
-
949
- UPDATE_TRACE_POINT();
950
- boost::container::vector<Callback> actions;
951
- {
952
- // Standard resource management boilerplate stuff...
953
- Pool *pool = getPool();
954
- boost::unique_lock<boost::mutex> lock(pool->syncher);
955
- if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) {
956
- return;
957
- }
958
-
959
- process->oobwStatus = Process::OOBW_NOT_ACTIVE;
960
- if (process->enabled == Process::DISABLED) {
961
- enable(process, actions);
962
- assignSessionsToGetWaiters(actions);
963
- }
964
-
965
- pool->fullVerifyInvariants();
966
-
967
- initiateNextOobwRequest();
968
- }
969
- UPDATE_TRACE_POINT();
970
- runAllActions(actions);
971
- actions.clear();
972
-
973
- UPDATE_TRACE_POINT();
974
- P_DEBUG("Finished OOBW request for process " << process->inspect());
975
- if (debug != NULL && debug->oobw) {
976
- debug->debugger->send("OOBW request finished");
977
- }
978
- }
979
-
980
- void
981
- Group::initiateNextOobwRequest() {
982
- ProcessList::const_iterator it, end = enabledProcesses.end();
983
- for (it = enabledProcesses.begin(); it != end; it++) {
984
- const ProcessPtr &process = *it;
985
- if (shouldInitiateOobw(process.get())) {
986
- // We keep an extra reference to processes to prevent premature destruction.
987
- ProcessPtr p = process;
988
- initiateOobw(p);
989
- return;
990
- }
991
- }
992
- }
993
-
994
- // The 'self' parameter is for keeping the current Group object alive while this thread is running.
995
- void
996
- Group::spawnThreadMain(GroupPtr self, SpawnerPtr spawner, Options options, unsigned int restartsInitiated) {
997
- spawnThreadRealMain(spawner, options, restartsInitiated);
998
- }
999
-
1000
- void
1001
- Group::spawnThreadRealMain(const SpawnerPtr &spawner, const Options &options, unsigned int restartsInitiated) {
1002
- TRACE_POINT();
1003
- this_thread::disable_interruption di;
1004
- this_thread::disable_syscall_interruption dsi;
1005
-
1006
- Pool *pool = getPool();
1007
- Pool::DebugSupportPtr debug = pool->debugSupport;
1008
-
1009
- bool done = false;
1010
- while (!done) {
1011
- bool shouldFail = false;
1012
- if (debug != NULL && debug->spawning) {
1013
- UPDATE_TRACE_POINT();
1014
- this_thread::restore_interruption ri(di);
1015
- this_thread::restore_syscall_interruption rsi(dsi);
1016
- this_thread::interruption_point();
1017
- string iteration;
1018
- {
1019
- LockGuard g(debug->syncher);
1020
- debug->spawnLoopIteration++;
1021
- iteration = toString(debug->spawnLoopIteration);
1022
- }
1023
- P_DEBUG("Begin spawn loop iteration " << iteration);
1024
- debug->debugger->send("Begin spawn loop iteration " +
1025
- iteration);
1026
-
1027
- vector<string> cases;
1028
- cases.push_back("Proceed with spawn loop iteration " + iteration);
1029
- cases.push_back("Fail spawn loop iteration " + iteration);
1030
- MessagePtr message = debug->messages->recvAny(cases);
1031
- shouldFail = message->name == "Fail spawn loop iteration " + iteration;
1032
- }
1033
-
1034
- SpawnObject spawnObject;
1035
- ProcessPtr process;
1036
- ExceptionPtr exception;
1037
- try {
1038
- UPDATE_TRACE_POINT();
1039
- this_thread::restore_interruption ri(di);
1040
- this_thread::restore_syscall_interruption rsi(dsi);
1041
- if (shouldFail) {
1042
- SpawnException e("Simulated failure");
1043
- processAndLogNewSpawnException(e, options, pool->getSpawnerConfig());
1044
- throw e;
1045
- } else {
1046
- spawnObject = spawner->spawn(options);
1047
- process = spawnObject.process;
1048
- process->setGroup(this);
1049
- }
1050
- } catch (const thread_interrupted &) {
1051
- break;
1052
- } catch (const tracable_exception &e) {
1053
- exception = copyException(e);
1054
- // Let other (unexpected) exceptions crash the program so
1055
- // gdb can generate a backtrace.
1056
- }
1057
-
1058
- UPDATE_TRACE_POINT();
1059
- ScopeGuard guard(boost::bind(Process::forceTriggerShutdownAndCleanup, process));
1060
- boost::unique_lock<boost::mutex> lock(pool->syncher);
1061
-
1062
- if (!isAlive()) {
1063
- if (process != NULL) {
1064
- P_DEBUG("Group is being shut down so dropping process " <<
1065
- process->inspect() << " which we just spawned and exiting spawn loop");
1066
- } else {
1067
- P_DEBUG("The group is being shut down. A process failed "
1068
- "to be spawned anyway, so ignoring this error and exiting "
1069
- "spawn loop");
1070
- }
1071
- // We stop immediately because any previously assumed invariants
1072
- // may have been violated.
1073
- break;
1074
- } else if (restartsInitiated != this->restartsInitiated) {
1075
- if (process != NULL) {
1076
- P_DEBUG("A restart was issued for the group, so dropping process " <<
1077
- process->inspect() << " which we just spawned and exiting spawn loop");
1078
- } else {
1079
- P_DEBUG("A restart was issued for the group. A process failed "
1080
- "to be spawned anyway, so ignoring this error and exiting "
1081
- "spawn loop");
1082
- }
1083
- // We stop immediately because any previously assumed invariants
1084
- // may have been violated.
1085
- break;
1086
- }
1087
-
1088
- verifyInvariants();
1089
- assert(m_spawning);
1090
- assert(processesBeingSpawned > 0);
1091
-
1092
- processesBeingSpawned--;
1093
- assert(processesBeingSpawned == 0);
1094
-
1095
- UPDATE_TRACE_POINT();
1096
- boost::container::vector<Callback> actions;
1097
- if (process != NULL) {
1098
- AttachResult result = attach(spawnObject, actions);
1099
- if (result == AR_OK) {
1100
- guard.clear();
1101
- if (getWaitlist.empty()) {
1102
- pool->assignSessionsToGetWaiters(actions);
1103
- } else {
1104
- assignSessionsToGetWaiters(actions);
1105
- }
1106
- P_DEBUG("New process count = " << enabledCount <<
1107
- ", remaining get waiters = " << getWaitlist.size());
1108
- } else {
1109
- done = true;
1110
- P_DEBUG("Unable to attach spawned process " << process->inspect());
1111
- if (result == AR_ANOTHER_GROUP_IS_WAITING_FOR_CAPACITY) {
1112
- pool->possiblySpawnMoreProcessesForExistingGroups();
1113
- }
1114
- }
1115
- } else {
1116
- // TODO: sure this is the best thing? if there are
1117
- // processes currently alive we should just use them.
1118
- if (enabledCount == 0) {
1119
- enableAllDisablingProcesses(actions);
1120
- }
1121
- Pool::assignExceptionToGetWaiters(getWaitlist, exception, actions);
1122
- pool->assignSessionsToGetWaiters(actions);
1123
- done = true;
1124
- }
1125
-
1126
- done = done
1127
- || (processLowerLimitsSatisfied() && getWaitlist.empty())
1128
- || processUpperLimitsReached()
1129
- || pool->atFullCapacityUnlocked();
1130
- m_spawning = !done;
1131
- if (done) {
1132
- P_DEBUG("Spawn loop done");
1133
- } else {
1134
- processesBeingSpawned++;
1135
- P_DEBUG("Continue spawning");
1136
- }
1137
-
1138
- UPDATE_TRACE_POINT();
1139
- pool->fullVerifyInvariants();
1140
- lock.unlock();
1141
- UPDATE_TRACE_POINT();
1142
- runAllActions(actions);
1143
- UPDATE_TRACE_POINT();
1144
- }
1145
-
1146
- if (debug != NULL && debug->spawning) {
1147
- debug->debugger->send("Spawn loop done");
1148
- }
1149
- }
1150
-
1151
- bool
1152
- Group::shouldSpawn() const {
1153
- return allowSpawn()
1154
- && (
1155
- !processLowerLimitsSatisfied()
1156
- || allEnabledProcessesAreTotallyBusy()
1157
- || !getWaitlist.empty()
1158
- );
1159
- }
1160
-
1161
- bool
1162
- Group::shouldSpawnForGetAction() const {
1163
- return enabledCount == 0 || shouldSpawn();
1164
- }
1165
-
1166
- void
1167
- Group::restart(const Options &options, RestartMethod method) {
1168
- boost::container::vector<Callback> actions;
1169
-
1170
- assert(isAlive());
1171
- P_DEBUG("Restarting group " << name);
1172
-
1173
- // If there is currently a restarter thread or a spawner thread active,
1174
- // the following tells them to abort their current work as soon as possible.
1175
- restartsInitiated++;
1176
-
1177
- processesBeingSpawned = 0;
1178
- m_spawning = false;
1179
- m_restarting = true;
1180
- uuid = generateUuid(getSuperGroup());
1181
- detachAll(actions);
1182
- getPool()->interruptableThreads.create_thread(
1183
- boost::bind(&Group::finalizeRestart, this, shared_from_this(),
1184
- this->options.copyAndPersist().clearPerRequestFields(),
1185
- options.copyAndPersist().clearPerRequestFields(),
1186
- method, getPool()->spawnerFactory, restartsInitiated, actions),
1187
- "Group restarter: " + name,
1188
- POOL_HELPER_THREAD_STACK_SIZE
1189
- );
1190
- }
1191
-
1192
- // The 'self' parameter is for keeping the current Group object alive while this thread is running.
1193
- void
1194
- Group::finalizeRestart(GroupPtr self,
1195
- Options oldOptions,
1196
- Options newOptions, RestartMethod method,
1197
- SpawnerFactoryPtr spawnerFactory,
1198
- unsigned int restartsInitiated,
1199
- boost::container::vector<Callback> postLockActions)
1200
- {
1201
- TRACE_POINT();
1202
-
1203
- Pool::runAllActions(postLockActions);
1204
- postLockActions.clear();
1205
-
1206
- this_thread::disable_interruption di;
1207
- this_thread::disable_syscall_interruption dsi;
1208
-
1209
- // Create a new spawner.
1210
- Options spawnerOptions = oldOptions;
1211
- resetOptions(newOptions, &spawnerOptions);
1212
- SpawnerPtr newSpawner = spawnerFactory->create(spawnerOptions);
1213
- SpawnerPtr oldSpawner;
1214
-
1215
- UPDATE_TRACE_POINT();
1216
- Pool *pool = getPool();
1217
-
1218
- Pool::DebugSupportPtr debug = pool->debugSupport;
1219
- if (debug != NULL && debug->restarting) {
1220
- this_thread::restore_interruption ri(di);
1221
- this_thread::restore_syscall_interruption rsi(dsi);
1222
- this_thread::interruption_point();
1223
- debug->debugger->send("About to end restarting");
1224
- debug->messages->recv("Finish restarting");
1225
- }
1226
-
1227
- ScopedLock l(pool->syncher);
1228
- if (!isAlive()) {
1229
- P_DEBUG("Group " << name << " is shutting down, so aborting restart");
1230
- return;
1231
- }
1232
- if (restartsInitiated != this->restartsInitiated) {
1233
- // Before this restart could be finalized, another restart command was given.
1234
- // The spawner we just created might be out of date now so we abort.
1235
- P_DEBUG("Restart of group " << name << " aborted because a new restart was initiated concurrently");
1236
- if (debug != NULL && debug->restarting) {
1237
- debug->debugger->send("Restarting aborted");
1238
- }
1239
- return;
1240
- }
1241
-
1242
- // Run some sanity checks.
1243
- pool->fullVerifyInvariants();
1244
- assert(m_restarting);
1245
- UPDATE_TRACE_POINT();
1246
-
1247
- // Atomically swap the new spawner with the old one.
1248
- resetOptions(newOptions);
1249
- oldSpawner = spawner;
1250
- spawner = newSpawner;
1251
-
1252
- m_restarting = false;
1253
- if (shouldSpawn()) {
1254
- spawn();
1255
- } else if (isWaitingForCapacity()) {
1256
- P_INFO("Group " << name << " is waiting for capacity to become available. "
1257
- "Trying to shutdown another idle process to free capacity...");
1258
- if (pool->forceFreeCapacity(this, postLockActions) != NULL) {
1259
- spawn();
1260
- } else {
1261
- P_INFO("There are no processes right now that are eligible "
1262
- "for shutdown. Will try again later.");
1263
- }
1264
- }
1265
- verifyInvariants();
1266
-
1267
- l.unlock();
1268
- oldSpawner.reset();
1269
- Pool::runAllActions(postLockActions);
1270
- P_DEBUG("Restart of group " << name << " done");
1271
- if (debug != NULL && debug->restarting) {
1272
- debug->debugger->send("Restarting done");
1273
- }
1274
- }
1275
-
1276
- /**
1277
- * The `immediately` parameter only has effect if the detached processes checker
1278
- * thread is active. It means that, if the thread is currently sleeping, it should
1279
- * wake up immediately and perform work.
1280
- */
1281
- void
1282
- Group::startCheckingDetachedProcesses(bool immediately) {
1283
- if (!detachedProcessesCheckerActive) {
1284
- P_DEBUG("Starting detached processes checker");
1285
- getPool()->nonInterruptableThreads.create_thread(
1286
- boost::bind(&Group::detachedProcessesCheckerMain, this, shared_from_this()),
1287
- "Detached processes checker: " + name,
1288
- POOL_HELPER_THREAD_STACK_SIZE
1289
- );
1290
- detachedProcessesCheckerActive = true;
1291
- } else if (detachedProcessesCheckerActive && immediately) {
1292
- detachedProcessesCheckerCond.notify_all();
1293
- }
1294
- }
1295
-
1296
- void
1297
- Group::detachedProcessesCheckerMain(GroupPtr self) {
1298
- TRACE_POINT();
1299
- Pool *pool = getPool();
1300
-
1301
- Pool::DebugSupportPtr debug = pool->debugSupport;
1302
- if (debug != NULL && debug->detachedProcessesChecker) {
1303
- debug->debugger->send("About to start detached processes checker");
1304
- debug->messages->recv("Proceed with starting detached processes checker");
1305
- }
1306
-
1307
- boost::unique_lock<boost::mutex> lock(pool->syncher);
1308
- while (true) {
1309
- assert(detachedProcessesCheckerActive);
1310
-
1311
- if (getLifeStatus() == SHUT_DOWN || this_thread::interruption_requested()) {
1312
- UPDATE_TRACE_POINT();
1313
- P_DEBUG("Stopping detached processes checker");
1314
- detachedProcessesCheckerActive = false;
1315
- break;
1316
- }
1317
-
1318
- UPDATE_TRACE_POINT();
1319
- if (!detachedProcesses.empty()) {
1320
- P_TRACE(2, "Checking whether any of the " << detachedProcesses.size() <<
1321
- " detached processes have exited...");
1322
- ProcessList::iterator it, end = detachedProcesses.end();
1323
- ProcessList processesToRemove;
1324
-
1325
- for (it = detachedProcesses.begin(); it != end; it++) {
1326
- const ProcessPtr process = *it;
1327
- switch (process->getLifeStatus()) {
1328
- case Process::ALIVE:
1329
- if (process->canTriggerShutdown()) {
1330
- P_DEBUG("Detached process " << process->inspect() <<
1331
- " has 0 active sessions now. Triggering shutdown.");
1332
- process->triggerShutdown();
1333
- assert(process->getLifeStatus() == Process::SHUTDOWN_TRIGGERED);
1334
- }
1335
- break;
1336
- case Process::SHUTDOWN_TRIGGERED:
1337
- if (process->canCleanup()) {
1338
- P_DEBUG("Detached process " << process->inspect() << " has shut down. Cleaning up associated resources.");
1339
- process->cleanup();
1340
- assert(process->getLifeStatus() == Process::DEAD);
1341
- processesToRemove.push_back(process);
1342
- } else if (process->shutdownTimeoutExpired()) {
1343
- P_WARN("Detached process " << process->inspect() <<
1344
- " didn't shut down within " PROCESS_SHUTDOWN_TIMEOUT_DISPLAY
1345
- ". Forcefully killing it with SIGKILL.");
1346
- kill(process->pid, SIGKILL);
1347
- }
1348
- break;
1349
- default:
1350
- P_BUG("Unknown 'lifeStatus' state " << (int) process->getLifeStatus());
1351
- }
1352
- }
1353
-
1354
- end = processesToRemove.end();
1355
- for (it = processesToRemove.begin(); it != end; it++) {
1356
- removeProcessFromList(*it, detachedProcesses);
1357
- }
1358
- }
1359
-
1360
- UPDATE_TRACE_POINT();
1361
- if (detachedProcesses.empty()) {
1362
- UPDATE_TRACE_POINT();
1363
- P_DEBUG("Stopping detached processes checker");
1364
- detachedProcessesCheckerActive = false;
1365
-
1366
- boost::container::vector<Callback> actions;
1367
- if (shutdownCanFinish()) {
1368
- UPDATE_TRACE_POINT();
1369
- finishShutdown(actions);
1370
- }
1371
-
1372
- verifyInvariants();
1373
- verifyExpensiveInvariants();
1374
- lock.unlock();
1375
- UPDATE_TRACE_POINT();
1376
- runAllActions(actions);
1377
- break;
1378
- } else {
1379
- UPDATE_TRACE_POINT();
1380
- verifyInvariants();
1381
- verifyExpensiveInvariants();
1382
- }
1383
-
1384
- // Not all processes can be shut down yet. Sleep for a while unless
1385
- // someone wakes us up.
1386
- UPDATE_TRACE_POINT();
1387
- detachedProcessesCheckerCond.timed_wait(lock,
1388
- posix_time::milliseconds(100));
1389
- }
1390
- }
1391
-
1392
- bool
1393
- Group::selfCheckingEnabled() const {
1394
- return getPool()->selfchecking;
1395
- }
1396
-
1397
- void
1398
- Group::wakeUpGarbageCollector() {
1399
- getPool()->garbageCollectionCond.notify_all();
1400
- }
1401
-
1402
- bool
1403
- Group::poolAtFullCapacity() const {
1404
- return getPool()->atFullCapacityUnlocked();
1405
- }
1406
-
1407
- bool
1408
- Group::anotherGroupIsWaitingForCapacity() const {
1409
- return findOtherGroupWaitingForCapacity() != NULL;
1410
- }
1411
-
1412
- Group *
1413
- Group::findOtherGroupWaitingForCapacity() const {
1414
- Pool *pool = getPool();
1415
- // TODO: this only works if SuperGroup can only have one Group.
1416
- // If we ever extend SuperGroup then this needs to be changed.
1417
- if (pool->superGroups.size() == 1) {
1418
- return NULL;
1419
- }
1420
-
1421
- SuperGroupMap::ConstIterator sg_it(pool->superGroups);
1422
- while (*sg_it != NULL) {
1423
- const SuperGroupPtr &superGroup = sg_it.getValue();
1424
- SuperGroup::GroupList::const_iterator g_it, g_end = superGroup->groups.end();
1425
- for (g_it = superGroup->groups.begin(); g_it != g_end; g_it++) {
1426
- if (g_it->get() != this && (*g_it)->isWaitingForCapacity()) {
1427
- return g_it->get();
1428
- }
1429
- }
1430
- sg_it.next();
1431
- }
1432
- return NULL;
1433
- }
1434
-
1435
- ProcessPtr
1436
- Group::poolForceFreeCapacity(const Group *exclude,
1437
- boost::container::vector<Callback> &postLockActions)
1438
- {
1439
- return getPool()->forceFreeCapacity(exclude, postLockActions);
1440
- }
1441
-
1442
- bool
1443
- Group::testOverflowRequestQueue() const {
1444
- // This has a performance penalty, although I'm not sure whether the penalty is
1445
- // any greater than a hash table lookup if I were to implement it in Options.
1446
- Pool::DebugSupportPtr debug = getPool()->debugSupport;
1447
- if (debug) {
1448
- return debug->testOverflowRequestQueue;
1449
- } else {
1450
- return false;
1451
- }
1452
- }
1453
-
1454
- void
1455
- Group::callAbortLongRunningConnectionsCallback(const ProcessPtr &process) {
1456
- Pool::AbortLongRunningConnectionsCallback callback =
1457
- getPool()->abortLongRunningConnectionsCallback;
1458
- if (callback != NULL) {
1459
- callback(process);
1460
- }
1461
- }
1462
-
1463
- psg_pool_t *
1464
- Group::getPallocPool() const {
1465
- return getPool()->palloc;
1466
- }
1467
-
1468
- const ResourceLocator &
1469
- Group::getResourceLocator() const {
1470
- return *getPool()->getSpawnerConfig()->resourceLocator;
1471
- }
1472
-
1473
- /* Given a hook name like "queue_full_error", we return HookScriptOptions filled in with this name and a spec
1474
- * (user settings that can be queried from agentsOptions using the external hook name that is prefixed with "hook_")
1475
- *
1476
- * @return false if the user parameters (agentsOptions) are not available (e.g. during ApplicationPool2_PoolTest)
1477
- */
1478
- bool
1479
- Group::prepareHookScriptOptions(HookScriptOptions &hsOptions, const char *name) {
1480
- SpawnerConfigPtr config = getPool()->getSpawnerConfig();
1481
- if (config->agentsOptions == NULL) {
1482
- return false;
1483
- }
1484
-
1485
- hsOptions.name = name;
1486
- string hookName = string("hook_") + name;
1487
- hsOptions.spec = config->agentsOptions->get(hookName, false);
1488
-
1489
- return true;
1490
- }
1491
-
1492
- // 'process' is not a reference so that bind(runAttachHooks, ...) causes the shared
1493
- // pointer reference to increment.
1494
- void
1495
- Group::runAttachHooks(const ProcessPtr process) const {
1496
- getPool()->runHookScripts("attached_process",
1497
- boost::bind(&Group::setupAttachOrDetachHook, this, process, _1));
1498
- }
1499
-
1500
- void
1501
- Group::runDetachHooks(const ProcessPtr process) const {
1502
- getPool()->runHookScripts("detached_process",
1503
- boost::bind(&Group::setupAttachOrDetachHook, this, process, _1));
1504
- }
1505
-
1506
- void
1507
- Group::setupAttachOrDetachHook(const ProcessPtr process, HookScriptOptions &options) const {
1508
- options.environment.push_back(make_pair("PASSENGER_PROCESS_PID", toString(process->pid)));
1509
- options.environment.push_back(make_pair("PASSENGER_APP_ROOT", this->options.appRoot));
1510
- }
1511
-
1512
- void
1513
- Group::generateSecret(const SuperGroup *superGroup, char *secret) {
1514
- superGroup->getPool()->getRandomGenerator()->generateAsciiString(secret, SECRET_SIZE);
1515
- }
1516
-
1517
- string
1518
- Group::generateUuid(const SuperGroup *superGroup) {
1519
- return superGroup->getPool()->getRandomGenerator()->generateAsciiString(20);
1520
- }
1521
-
1522
-
1523
- Pool *
1524
- Process::getPool() const {
1525
- assert(getLifeStatus() != DEAD);
1526
- return getGroup()->getPool();
1527
- }
1528
-
1529
- SuperGroup *
1530
- Process::getSuperGroup() const {
1531
- assert(getLifeStatus() != DEAD);
1532
- return getGroup()->getSuperGroup();
1533
- }
1534
-
1535
- StaticString
1536
- Process::getGroupSecret() const {
1537
- return StaticString(getGroup()->secret, Group::SECRET_SIZE);
1538
- }
1539
-
1540
- SessionPtr
1541
- Process::createSessionObject(Socket *socket) {
1542
- Pool *pool = NULL;
1543
- Group *group = getGroup();
1544
- if (group != NULL) {
1545
- // Backpointers are normally never NULL, but they can be
1546
- // NULL in unit tests
1547
- SuperGroup *superGroup = group->getSuperGroup();
1548
- if (superGroup != NULL) {
1549
- pool = superGroup->getPool();
1550
- }
1551
- }
1552
-
1553
- Session *session;
1554
- if (OXT_LIKELY(pool != NULL)) {
1555
- {
1556
- LockGuard l(pool->sessionObjectPoolSyncher);
1557
- session = pool->sessionObjectPool.malloc();
1558
- }
1559
- session = new (session) Session(pool, this, socket);
1560
- } else {
1561
- session = new Session(NULL, this, socket);
1562
- }
1563
- return SessionPtr(session, false);
1564
- }
1565
-
1566
- string
1567
- Process::inspect() const {
1568
- assert(getLifeStatus() != DEAD);
1569
- stringstream result;
1570
- result << "(pid=" << pid;
1571
- Group *group = getGroup();
1572
- if (group != NULL) {
1573
- // This Process hasn't been attached to a Group yet.
1574
- result << ", group=" << group->name;
1575
- }
1576
- result << ")";
1577
- return result.str();
1578
- }
1579
-
1580
-
1581
- StaticString
1582
- Session::getGroupSecret() const {
1583
- return StaticString(getGroup()->secret, Group::SECRET_SIZE);
1584
- }
1585
-
1586
- pid_t
1587
- Session::getPid() const {
1588
- return getProcess()->pid;
1589
- }
1590
-
1591
- StaticString
1592
- Session::getGupid() const {
1593
- const Process *process = getProcess();
1594
- return StaticString(process->gupid, process->gupidSize);
1595
- }
1596
-
1597
- unsigned int
1598
- Session::getStickySessionId() const {
1599
- return getProcess()->stickySessionId;
1600
- }
1601
-
1602
- Group *
1603
- Session::getGroup() const {
1604
- return getProcess()->getGroup();
1605
- }
1606
-
1607
326
  void
1608
327
  Session::requestOOBW() {
1609
328
  ProcessPtr process = getProcess()->shared_from_this();
@@ -1611,107 +330,6 @@ Session::requestOOBW() {
1611
330
  process->getGroup()->requestOOBW(process);
1612
331
  }
1613
332
 
1614
- int
1615
- Session::kill(int signo) {
1616
- return getProcess()->kill(signo);
1617
- }
1618
-
1619
- void
1620
- Session::destroySelf() const {
1621
- Pool *pool = this->pool;
1622
- // Backpointers are normally never NULL, but they can be
1623
- // NULL in unit tests
1624
- if (pool != NULL) {
1625
- this->~Session();
1626
- LockGuard l(pool->sessionObjectPoolSyncher);
1627
- pool->sessionObjectPool.free(const_cast<Session *>(this));
1628
- } else {
1629
- delete this;
1630
- }
1631
- }
1632
-
1633
-
1634
- PipeWatcher::DataCallback PipeWatcher::onData;
1635
-
1636
- PipeWatcher::PipeWatcher(const FileDescriptor &_fd, const char *_name, pid_t _pid)
1637
- : fd(_fd),
1638
- name(_name),
1639
- pid(_pid)
1640
- {
1641
- started = false;
1642
- }
1643
-
1644
- void
1645
- PipeWatcher::initialize() {
1646
- oxt::thread(boost::bind(threadMain, shared_from_this()),
1647
- "PipeWatcher: PID " + toString(pid) + " " + name + ", fd " + toString(fd),
1648
- POOL_HELPER_THREAD_STACK_SIZE);
1649
- }
1650
-
1651
- void
1652
- PipeWatcher::start() {
1653
- boost::lock_guard<boost::mutex> lock(startSyncher);
1654
- started = true;
1655
- startCond.notify_all();
1656
- }
1657
-
1658
- void
1659
- PipeWatcher::threadMain(boost::shared_ptr<PipeWatcher> self) {
1660
- TRACE_POINT();
1661
- self->threadMain();
1662
- }
1663
-
1664
- void
1665
- PipeWatcher::threadMain() {
1666
- TRACE_POINT();
1667
- {
1668
- boost::unique_lock<boost::mutex> lock(startSyncher);
1669
- while (!started) {
1670
- startCond.wait(lock);
1671
- }
1672
- }
1673
-
1674
- UPDATE_TRACE_POINT();
1675
- while (!this_thread::interruption_requested()) {
1676
- char buf[1024 * 8];
1677
- ssize_t ret;
1678
-
1679
- UPDATE_TRACE_POINT();
1680
- ret = syscalls::read(fd, buf, sizeof(buf));
1681
- if (ret == 0) {
1682
- break;
1683
- } else if (ret == -1) {
1684
- UPDATE_TRACE_POINT();
1685
- if (errno == ECONNRESET) {
1686
- break;
1687
- } else if (errno != EAGAIN) {
1688
- int e = errno;
1689
- P_WARN("Cannot read from process " << pid << " " << name <<
1690
- ": " << strerror(e) << " (errno=" << e << ")");
1691
- break;
1692
- }
1693
- } else if (ret == 1 && buf[0] == '\n') {
1694
- UPDATE_TRACE_POINT();
1695
- printAppOutput(pid, name, "", 0);
1696
- } else {
1697
- UPDATE_TRACE_POINT();
1698
- vector<StaticString> lines;
1699
- ssize_t ret2 = ret;
1700
- if (ret2 > 0 && buf[ret2 - 1] == '\n') {
1701
- ret2--;
1702
- }
1703
- split(StaticString(buf, ret2), '\n', lines);
1704
- foreach (const StaticString line, lines) {
1705
- printAppOutput(pid, name, line.data(), line.size());
1706
- }
1707
- }
1708
-
1709
- if (onData != NULL) {
1710
- onData(buf, ret);
1711
- }
1712
- }
1713
- }
1714
-
1715
333
 
1716
334
  } // namespace ApplicationPool2
1717
335
  } // namespace Passenger