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,276 @@
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/Pool.h>
26
+
27
+ /*************************************************************************
28
+ *
29
+ * Group data structure utility functions for ApplicationPool2::Pool
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
+ const GroupPtr
48
+ Pool::getGroup(const char *name) {
49
+ GroupPtr *group;
50
+ if (groups.lookup(name, &group)) {
51
+ return *group;
52
+ } else {
53
+ return GroupPtr();
54
+ }
55
+ }
56
+
57
+ Group *
58
+ Pool::findMatchingGroup(const Options &options) {
59
+ GroupPtr *group;
60
+ if (groups.lookup(options.getAppGroupName(), &group)) {
61
+ return group->get();
62
+ } else {
63
+ return NULL;
64
+ }
65
+ }
66
+
67
+ GroupPtr
68
+ Pool::createGroup(const Options &options) {
69
+ GroupPtr group = boost::make_shared<Group>(this, options);
70
+ group->initialize();
71
+ groups.insert(options.getAppGroupName(), group);
72
+ wakeupGarbageCollector();
73
+ return group;
74
+ }
75
+
76
+ GroupPtr
77
+ Pool::createGroupAndAsyncGetFromIt(const Options &options,
78
+ const GetCallback &callback, boost::container::vector<Callback> &postLockActions)
79
+ {
80
+ GroupPtr group = createGroup(options);
81
+ SessionPtr session = group->get(options, callback,
82
+ postLockActions);
83
+ /* If !options.noop, then the callback should now have been put on the
84
+ * wait list, unless something has changed and we forgot to update
85
+ * some code here...
86
+ */
87
+ if (session != NULL) {
88
+ assert(options.noop);
89
+ postLockActions.push_back(boost::bind(GetCallback::call,
90
+ callback, session, ExceptionPtr()));
91
+ }
92
+ return group;
93
+ }
94
+
95
+ /**
96
+ * Forcefully destroys and detaches the given Group. After detaching
97
+ * the Group may have a non-empty getWaitlist so be sure to do
98
+ * something with it.
99
+ *
100
+ * Also, one of the post lock actions can potentially perform a long-running
101
+ * operation, so running them in a thread is advised.
102
+ */
103
+ void
104
+ Pool::forceDetachGroup(const GroupPtr &group,
105
+ const Callback &callback,
106
+ boost::container::vector<Callback> &postLockActions)
107
+ {
108
+ assert(group->getWaitlist.empty());
109
+ const GroupPtr p = group; // Prevent premature destruction.
110
+ bool removed = groups.erase(group->getName());
111
+ assert(removed);
112
+ (void) removed; // Shut up compiler warning.
113
+ group->shutdown(callback, postLockActions);
114
+ }
115
+
116
+ void
117
+ Pool::syncDetachGroupCallback(boost::shared_ptr<DetachGroupWaitTicket> ticket) {
118
+ LockGuard l(ticket->syncher);
119
+ ticket->done = true;
120
+ ticket->cond.notify_one();
121
+ }
122
+
123
+ void
124
+ Pool::waitDetachGroupCallback(boost::shared_ptr<DetachGroupWaitTicket> ticket) {
125
+ ScopedLock l(ticket->syncher);
126
+ while (!ticket->done) {
127
+ ticket->cond.wait(l);
128
+ }
129
+ }
130
+
131
+
132
+ /****************************
133
+ *
134
+ * Public methods
135
+ *
136
+ ****************************/
137
+
138
+
139
+ GroupPtr
140
+ Pool::findOrCreateGroup(const Options &options) {
141
+ Options options2 = options;
142
+ options2.noop = true;
143
+
144
+ Ticket ticket;
145
+ {
146
+ LockGuard l(syncher);
147
+ GroupPtr *group;
148
+ if (!groups.lookup(options.getAppGroupName(), &group)) {
149
+ // Forcefully create Group, don't care whether resource limits
150
+ // actually allow it.
151
+ createGroup(options);
152
+ }
153
+ }
154
+ return get(options2, &ticket)->getGroup()->shared_from_this();
155
+ }
156
+
157
+ GroupPtr
158
+ Pool::findGroupByApiKey(const StaticString &value, bool lock) const {
159
+ DynamicScopedLock l(syncher, lock);
160
+ GroupMap::ConstIterator g_it(groups);
161
+ while (*g_it != NULL) {
162
+ const GroupPtr &group = g_it.getValue();
163
+ if (group->getApiKey() == value) {
164
+ return group;
165
+ }
166
+ g_it.next();
167
+ }
168
+ return GroupPtr();
169
+ }
170
+
171
+ bool
172
+ Pool::detachGroupByName(const HashedStaticString &name) {
173
+ TRACE_POINT();
174
+ ScopedLock l(syncher);
175
+ GroupPtr group = groups.lookupCopy(name);
176
+
177
+ if (OXT_LIKELY(group != NULL)) {
178
+ P_ASSERT_EQ(group->getName(), name);
179
+ UPDATE_TRACE_POINT();
180
+ verifyInvariants();
181
+ verifyExpensiveInvariants();
182
+
183
+ boost::container::vector<Callback> actions;
184
+ boost::shared_ptr<DetachGroupWaitTicket> ticket =
185
+ boost::make_shared<DetachGroupWaitTicket>();
186
+ ExceptionPtr exception = copyException(
187
+ GetAbortedException("The containing Group was detached."));
188
+
189
+ assignExceptionToGetWaiters(group->getWaitlist,
190
+ exception, actions);
191
+ forceDetachGroup(group,
192
+ boost::bind(syncDetachGroupCallback, ticket),
193
+ actions);
194
+ possiblySpawnMoreProcessesForExistingGroups();
195
+
196
+ verifyInvariants();
197
+ verifyExpensiveInvariants();
198
+
199
+ l.unlock();
200
+ UPDATE_TRACE_POINT();
201
+ runAllActions(actions);
202
+ actions.clear();
203
+
204
+ UPDATE_TRACE_POINT();
205
+ ScopedLock l2(ticket->syncher);
206
+ while (!ticket->done) {
207
+ ticket->cond.wait(l2);
208
+ }
209
+ return true;
210
+ } else {
211
+ return false;
212
+ }
213
+ }
214
+
215
+ bool
216
+ Pool::detachGroupByApiKey(const StaticString &value) {
217
+ ScopedLock l(syncher);
218
+ GroupPtr group = findGroupByApiKey(value, false);
219
+ if (group != NULL) {
220
+ string name = group->getName();
221
+ group.reset();
222
+ l.unlock();
223
+ return detachGroupByName(name);
224
+ } else {
225
+ return false;
226
+ }
227
+ }
228
+
229
+ bool
230
+ Pool::restartGroupByName(const StaticString &name, const RestartOptions &options) {
231
+ ScopedLock l(syncher);
232
+ GroupMap::ConstIterator g_it(groups);
233
+ while (*g_it != NULL) {
234
+ const GroupPtr &group = g_it.getValue();
235
+ if (name == group->getName()) {
236
+ if (!group->authorizeByUid(options.uid)
237
+ && !group->authorizeByApiKey(options.apiKey))
238
+ {
239
+ throw SecurityException("Operation unauthorized");
240
+ }
241
+ if (!group->restarting()) {
242
+ group->restart(group->options, options.method);
243
+ }
244
+ return true;
245
+ }
246
+ g_it.next();
247
+ }
248
+
249
+ return false;
250
+ }
251
+
252
+ unsigned int
253
+ Pool::restartGroupsByAppRoot(const StaticString &appRoot, const RestartOptions &options) {
254
+ ScopedLock l(syncher);
255
+ GroupMap::ConstIterator g_it(groups);
256
+ unsigned int result = 0;
257
+
258
+ while (*g_it != NULL) {
259
+ const GroupPtr &group = g_it.getValue();
260
+ if (appRoot == group->options.appRoot) {
261
+ if (group->authorizeByUid(options.uid)
262
+ || group->authorizeByApiKey(options.apiKey))
263
+ {
264
+ result++;
265
+ group->restart(group->options, options.method);
266
+ }
267
+ }
268
+ g_it.next();
269
+ }
270
+
271
+ return result;
272
+ }
273
+
274
+
275
+ } // namespace ApplicationPool2
276
+ } // namespace Passenger
@@ -0,0 +1,145 @@
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/Pool.h>
26
+
27
+ /*************************************************************************
28
+ *
29
+ * Initialization and shutdown-related code for ApplicationPool2::Pool
30
+ *
31
+ *************************************************************************/
32
+
33
+ namespace Passenger {
34
+ namespace ApplicationPool2 {
35
+
36
+ using namespace std;
37
+ using namespace boost;
38
+
39
+
40
+ Pool::Pool(const SpawningKit::FactoryPtr &spawningKitFactory,
41
+ const VariantMap *agentsOptions)
42
+ : abortLongRunningConnectionsCallback(NULL)
43
+ {
44
+ context.setSpawningKitFactory(spawningKitFactory);
45
+ context.finalize();
46
+
47
+ this->agentsOptions = agentsOptions;
48
+
49
+ try {
50
+ systemMetricsCollector.collect(systemMetrics);
51
+ } catch (const RuntimeException &e) {
52
+ P_WARN("Unable to collect system metrics: " << e.what());
53
+ }
54
+
55
+ lifeStatus = ALIVE;
56
+ max = 6;
57
+ maxIdleTime = 60 * 1000000;
58
+ selfchecking = true;
59
+ palloc = psg_create_pool(PSG_DEFAULT_POOL_SIZE);
60
+
61
+ // The following code only serve to instantiate certain inline methods
62
+ // so that they can be invoked from gdb.
63
+ (void) GroupPtr().get();
64
+ (void) ProcessPtr().get();
65
+ (void) SessionPtr().get();
66
+ }
67
+
68
+ Pool::~Pool() {
69
+ if (lifeStatus != SHUT_DOWN) {
70
+ P_BUG("You must call Pool::destroy() before actually destroying the Pool object!");
71
+ }
72
+ psg_destroy_pool(palloc);
73
+ }
74
+
75
+ /** Must be called right after construction. */
76
+ void
77
+ Pool::initialize() {
78
+ LockGuard l(syncher);
79
+ initializeAnalyticsCollection();
80
+ initializeGarbageCollection();
81
+ }
82
+
83
+ void
84
+ Pool::initDebugging() {
85
+ LockGuard l(syncher);
86
+ debugSupport = boost::make_shared<DebugSupport>();
87
+ }
88
+
89
+ /**
90
+ * Should be called right after the agent has received
91
+ * the message to exit gracefully. This will tell processes to
92
+ * abort any long-running connections, e.g. WebSocket connections,
93
+ * because the RequestHandler has to wait until all connections are
94
+ * finished before proceeding with shutdown.
95
+ */
96
+ void
97
+ Pool::prepareForShutdown() {
98
+ TRACE_POINT();
99
+ ScopedLock lock(syncher);
100
+ assert(lifeStatus == ALIVE);
101
+ lifeStatus = PREPARED_FOR_SHUTDOWN;
102
+ if (abortLongRunningConnectionsCallback != NULL) {
103
+ vector<ProcessPtr> processes = getProcesses(false);
104
+ foreach (ProcessPtr process, processes) {
105
+ // Ensure that the process is not immediately respawned.
106
+ process->getGroup()->options.minProcesses = 0;
107
+ abortLongRunningConnectionsCallback(process);
108
+ }
109
+ }
110
+ }
111
+
112
+ /** Must be called right before destruction. */
113
+ void
114
+ Pool::destroy() {
115
+ TRACE_POINT();
116
+ ScopedLock lock(syncher);
117
+ assert(lifeStatus == ALIVE || lifeStatus == PREPARED_FOR_SHUTDOWN);
118
+
119
+ lifeStatus = SHUTTING_DOWN;
120
+
121
+ while (!groups.empty()) {
122
+ GroupPtr *group;
123
+ groups.lookupRandom(NULL, &group);
124
+ string name = group->get()->getName().toString();
125
+ lock.unlock();
126
+ detachGroupByName(name);
127
+ lock.lock();
128
+ }
129
+
130
+ UPDATE_TRACE_POINT();
131
+ lock.unlock();
132
+ interruptableThreads.interrupt_and_join_all();
133
+ nonInterruptableThreads.join_all();
134
+ lock.lock();
135
+
136
+ lifeStatus = SHUT_DOWN;
137
+
138
+ UPDATE_TRACE_POINT();
139
+ verifyInvariants();
140
+ verifyExpensiveInvariants();
141
+ }
142
+
143
+
144
+ } // namespace ApplicationPool2
145
+ } // namespace Passenger
@@ -0,0 +1,244 @@
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/Pool.h>
26
+
27
+ namespace Passenger {
28
+ namespace ApplicationPool2 {
29
+
30
+ using namespace std;
31
+ using namespace boost;
32
+
33
+
34
+ // 'lockNow == false' may only be used during unit tests. Normally we
35
+ // should never call the callback while holding the lock.
36
+ void
37
+ Pool::asyncGet(const Options &options, const GetCallback &callback, bool lockNow) {
38
+ DynamicScopedLock lock(syncher, lockNow);
39
+
40
+ assert(lifeStatus == ALIVE || lifeStatus == PREPARED_FOR_SHUTDOWN);
41
+ verifyInvariants();
42
+ P_TRACE(2, "asyncGet(appGroupName=" << options.getAppGroupName() << ")");
43
+ boost::container::vector<Callback> actions;
44
+
45
+ Group *existingGroup = findMatchingGroup(options);
46
+ if (OXT_LIKELY(existingGroup != NULL)) {
47
+ /* Best case: the app group is already in the pool. Let's use it. */
48
+ P_TRACE(2, "Found existing Group");
49
+ existingGroup->verifyInvariants();
50
+ SessionPtr session = existingGroup->get(options, callback, actions);
51
+ existingGroup->verifyInvariants();
52
+ verifyInvariants();
53
+ P_TRACE(2, "asyncGet() finished");
54
+ if (lockNow) {
55
+ lock.unlock();
56
+ }
57
+ if (session != NULL) {
58
+ callback(session, ExceptionPtr());
59
+ }
60
+
61
+ } else if (!atFullCapacityUnlocked()) {
62
+ /* The app super group isn't in the pool and we have enough free
63
+ * resources to make a new one.
64
+ */
65
+ P_DEBUG("Spawning new Group");
66
+ GroupPtr group = createGroupAndAsyncGetFromIt(options,
67
+ callback, actions);
68
+ group->verifyInvariants();
69
+ verifyInvariants();
70
+ P_DEBUG("asyncGet() finished");
71
+
72
+ } else {
73
+ /* Uh oh, the app super group isn't in the pool but we don't
74
+ * have the resources to make a new one. The sysadmin should
75
+ * configure the system to let something like this happen
76
+ * as least as possible, but let's try to handle it as well
77
+ * as we can.
78
+ */
79
+ ProcessPtr freedProcess = forceFreeCapacity(NULL, actions);
80
+ if (freedProcess == NULL) {
81
+ /* No process is eligible for killing. This could happen if, for example,
82
+ * all (super)groups are currently initializing/restarting/spawning/etc.
83
+ * We have no choice but to satisfy this get() action later when resources
84
+ * become available.
85
+ */
86
+ P_DEBUG("Could not free a process; putting request to top-level getWaitlist");
87
+ getWaitlist.push_back(GetWaiter(
88
+ options.copyAndPersist().detachFromUnionStationTransaction(),
89
+ callback));
90
+ } else {
91
+ /* Now that a process has been trashed we can create
92
+ * the missing Group.
93
+ */
94
+ P_DEBUG("Creating new Group");
95
+ GroupPtr group = createGroup(options);
96
+ SessionPtr session = group->get(options, callback,
97
+ actions);
98
+ /* The Group is now spawning a process so the callback
99
+ * should now have been put on the wait list,
100
+ * unless something has changed and we forgot to update
101
+ * some code here or if options.noop...
102
+ */
103
+ if (session != NULL) {
104
+ assert(options.noop);
105
+ actions.push_back(boost::bind(GetCallback::call,
106
+ callback, session, ExceptionPtr()));
107
+ }
108
+ freedProcess->getGroup()->verifyInvariants();
109
+ group->verifyInvariants();
110
+ }
111
+
112
+ assert(atFullCapacityUnlocked());
113
+ verifyInvariants();
114
+ verifyExpensiveInvariants();
115
+ P_TRACE(2, "asyncGet() finished");
116
+ }
117
+
118
+ if (!actions.empty()) {
119
+ if (lockNow) {
120
+ if (lock.owns_lock()) {
121
+ lock.unlock();
122
+ }
123
+ runAllActions(actions);
124
+ } else {
125
+ // This state is not allowed. If we reach
126
+ // here then it probably indicates a bug in
127
+ // the test suite.
128
+ abort();
129
+ }
130
+ }
131
+ }
132
+
133
+ // TODO: 'ticket' should be a boost::shared_ptr for interruption-safety.
134
+ SessionPtr
135
+ Pool::get(const Options &options, Ticket *ticket) {
136
+ ticket->session.reset();
137
+ ticket->exception.reset();
138
+
139
+ GetCallback callback;
140
+ callback.func = syncGetCallback;
141
+ callback.userData = ticket;
142
+ asyncGet(options, callback);
143
+
144
+ ScopedLock lock(ticket->syncher);
145
+ while (ticket->session == NULL && ticket->exception == NULL) {
146
+ ticket->cond.wait(lock);
147
+ }
148
+ lock.unlock();
149
+
150
+ if (OXT_LIKELY(ticket->session != NULL)) {
151
+ SessionPtr session = ticket->session;
152
+ ticket->session.reset();
153
+ return session;
154
+ } else {
155
+ rethrowException(ticket->exception);
156
+ return SessionPtr(); // Shut up compiler warning.
157
+ }
158
+ }
159
+
160
+ void
161
+ Pool::setMax(unsigned int max) {
162
+ ScopedLock l(syncher);
163
+ assert(max > 0);
164
+ fullVerifyInvariants();
165
+ bool bigger = max > this->max;
166
+ this->max = max;
167
+ if (bigger) {
168
+ /* If there are clients waiting for resources
169
+ * to become free, spawn more processes now that
170
+ * we have the capacity.
171
+ *
172
+ * We favor waiters on the pool over waiters on the
173
+ * the groups because the latter already have the
174
+ * resources to eventually complete. Favoring waiters
175
+ * on the pool should be fairer.
176
+ */
177
+ boost::container::vector<Callback> actions;
178
+ assignSessionsToGetWaiters(actions);
179
+ possiblySpawnMoreProcessesForExistingGroups();
180
+
181
+ fullVerifyInvariants();
182
+ l.unlock();
183
+ runAllActions(actions);
184
+ } else {
185
+ fullVerifyInvariants();
186
+ }
187
+ }
188
+
189
+ void
190
+ Pool::setMaxIdleTime(unsigned long long value) {
191
+ LockGuard l(syncher);
192
+ maxIdleTime = value;
193
+ wakeupGarbageCollector();
194
+ }
195
+
196
+ void
197
+ Pool::enableSelfChecking(bool enabled) {
198
+ LockGuard l(syncher);
199
+ selfchecking = enabled;
200
+ }
201
+
202
+ /**
203
+ * Checks whether at least one process is being spawned.
204
+ */
205
+ bool
206
+ Pool::isSpawning(bool lock) const {
207
+ DynamicScopedLock l(syncher, lock);
208
+ GroupMap::ConstIterator g_it(groups);
209
+ while (*g_it != NULL) {
210
+ const GroupPtr &group = g_it.getValue();
211
+ if (group->spawning()) {
212
+ return true;
213
+ }
214
+ g_it.next();
215
+ }
216
+ return false;
217
+ }
218
+
219
+ bool
220
+ Pool::authorizeByApiKey(const ApiKey &key, bool lock) const {
221
+ return key.isSuper() || findGroupByApiKey(key.toStaticString(), lock) != NULL;
222
+ }
223
+
224
+ bool
225
+ Pool::authorizeByUid(uid_t uid, bool lock) const {
226
+ if (uid == 0 || uid == geteuid()) {
227
+ return true;
228
+ }
229
+
230
+ DynamicScopedLock l(syncher, lock);
231
+ GroupMap::ConstIterator g_it(groups);
232
+ while (*g_it != NULL) {
233
+ const GroupPtr &group = g_it.getValue();
234
+ if (group->authorizeByUid(uid)) {
235
+ return true;
236
+ }
237
+ g_it.next();
238
+ }
239
+ return false;
240
+ }
241
+
242
+
243
+ } // namespace ApplicationPool2
244
+ } // namespace Passenger