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
@@ -22,16 +22,18 @@
22
22
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
23
  * THE SOFTWARE.
24
24
  */
25
- #ifndef _PASSENGER_SERVER_AGENT_ADMIN_SERVER_H_
26
- #define _PASSENGER_SERVER_AGENT_ADMIN_SERVER_H_
25
+ #ifndef _PASSENGER_SERVER_AGENT_API_SERVER_H_
26
+ #define _PASSENGER_SERVER_AGENT_API_SERVER_H_
27
27
 
28
28
  #include <boost/regex.hpp>
29
29
  #include <oxt/thread.hpp>
30
30
  #include <sstream>
31
31
  #include <string>
32
32
  #include <cstring>
33
+ #include <sys/types.h>
33
34
 
34
35
  #include <agents/HelperAgent/RequestHandler.h>
36
+ #include <agents/ApiServerUtils.h>
35
37
  #include <ApplicationPool2/Pool.h>
36
38
  #include <ServerKit/HttpServer.h>
37
39
  #include <DataStructures/LString.h>
@@ -42,6 +44,8 @@
42
44
  #include <Utils/StrIntUtils.h>
43
45
  #include <Utils/modp_b64.h>
44
46
  #include <Utils/json.h>
47
+ #include <Utils/BufferedIO.h>
48
+ #include <Utils/MessageIO.h>
45
49
 
46
50
  namespace Passenger {
47
51
  namespace ServerAgent {
@@ -53,26 +57,14 @@ class Request: public ServerKit::BaseHttpRequest {
53
57
  public:
54
58
  string body;
55
59
  Json::Value jsonBody;
60
+ Authorization authorization;
56
61
 
57
62
  DEFINE_SERVER_KIT_BASE_HTTP_REQUEST_FOOTER(Request);
58
63
  };
59
64
 
60
- class AdminServer: public ServerKit::HttpServer<AdminServer, ServerKit::HttpClient<Request> > {
61
- public:
62
- enum PrivilegeLevel {
63
- NONE,
64
- READONLY,
65
- FULL
66
- };
67
-
68
- struct Authorization {
69
- PrivilegeLevel level;
70
- string username;
71
- string password;
72
- };
73
-
65
+ class ApiServer: public ServerKit::HttpServer<ApiServer, ServerKit::HttpClient<Request> > {
74
66
  private:
75
- typedef ServerKit::HttpServer<AdminServer, ServerKit::HttpClient<Request> > ParentClass;
67
+ typedef ServerKit::HttpServer<ApiServer, ServerKit::HttpClient<Request> > ParentClass;
76
68
  typedef ServerKit::HttpClient<Request> Client;
77
69
  typedef ServerKit::HeaderTable HeaderTable;
78
70
 
@@ -82,57 +74,6 @@ private:
82
74
  return boost::regex_match(str.data(), str.data() + str.size(), e);
83
75
  }
84
76
 
85
- bool parseAuthorizationHeader(Request *req, string &username,
86
- string &password) const
87
- {
88
- const LString *auth = req->headers.lookup("authorization");
89
-
90
- if (auth == NULL || auth->size <= 6 || !psg_lstr_cmp(auth, "Basic ", 6)) {
91
- return false;
92
- }
93
-
94
- auth = psg_lstr_make_contiguous(auth, req->pool);
95
- string authData = modp::b64_decode(
96
- auth->start->data + sizeof("Basic ") - 1,
97
- auth->size - (sizeof("Basic ") - 1));
98
- string::size_type pos = authData.find(':');
99
- if (pos == string::npos) {
100
- return false;
101
- }
102
-
103
- username = authData.substr(0, pos);
104
- password = authData.substr(pos + 1);
105
- return true;
106
- }
107
-
108
- const Authorization *lookupAuthorizationRecord(const StaticString &username) const {
109
- vector<Authorization>::const_iterator it, end = authorizations.end();
110
-
111
- for (it = authorizations.begin(); it != end; it++) {
112
- if (it->username == username) {
113
- return &(*it);
114
- }
115
- }
116
-
117
- return NULL;
118
- }
119
-
120
- bool authorize(Client *client, Request *req, PrivilegeLevel level) const {
121
- if (authorizations.empty()) {
122
- return true;
123
- }
124
-
125
- string username, password;
126
- if (!parseAuthorizationHeader(req, username, password)) {
127
- return false;
128
- }
129
-
130
- const Authorization *auth = lookupAuthorizationRecord(username);
131
- return auth != NULL
132
- && auth->level >= level
133
- && constantTimeCompare(password, auth->password);
134
- }
135
-
136
77
  int extractThreadNumberFromClientName(const string &clientName) const {
137
78
  boost::smatch results;
138
79
  boost::regex re("^([0-9]+)-.*");
@@ -146,42 +87,48 @@ private:
146
87
  return stringToUint(results.str(1));
147
88
  }
148
89
 
149
- static VariantMap parseQueryString(const StaticString &query) {
150
- VariantMap params;
151
- const char *pos = query.data();
152
- const char *end = query.data() + query.size();
153
-
154
- while (pos < end) {
155
- const char *assignmentPos = (const char *) memchr(pos, '=', end - pos);
156
- if (assignmentPos != NULL) {
157
- string name = urldecode(StaticString(pos, assignmentPos - pos));
158
- const char *sepPos = (const char *) memchr(assignmentPos + 1, '&',
159
- end - assignmentPos - 1);
160
- if (sepPos != NULL) {
161
- string value = urldecode(StaticString(assignmentPos + 1,
162
- sepPos - assignmentPos - 1));
163
- params.set(name, value);
164
- pos = sepPos + 1;
165
- } else {
166
- StaticString value(assignmentPos + 1, end - assignmentPos - 1);
167
- params.set(name, value);
168
- pos = end;
169
- }
170
- } else {
171
- throw SyntaxError("Invalid query string format");
172
- }
173
- }
174
-
175
- return params;
176
- }
177
-
178
90
  static void disconnectClient(RequestHandler *rh, string clientName) {
179
91
  rh->disconnect(clientName);
180
92
  }
181
93
 
94
+ void route(Client *client, Request *req, const StaticString &path) {
95
+ if (path == P_STATIC_STRING("/server.json")) {
96
+ processServerStatus(client, req);
97
+ } else if (regex_match(path, serverConnectionPath)) {
98
+ processServerConnectionOperation(client, req);
99
+ } else if (path == P_STATIC_STRING("/pool.xml")) {
100
+ processPoolStatusXml(client, req);
101
+ } else if (path == P_STATIC_STRING("/pool.txt")) {
102
+ processPoolStatusTxt(client, req);
103
+ } else if (path == P_STATIC_STRING("/pool/restart_app_group.json")) {
104
+ processPoolRestartAppGroup(client, req);
105
+ } else if (path == P_STATIC_STRING("/pool/detach_process.json")) {
106
+ processPoolDetachProcess(client, req);
107
+ } else if (path == P_STATIC_STRING("/backtraces.txt")) {
108
+ apiServerProcessBacktraces(this, client, req);
109
+ } else if (path == P_STATIC_STRING("/ping.json")) {
110
+ apiServerProcessPing(this, client, req);
111
+ } else if (path == P_STATIC_STRING("/version.json")) {
112
+ apiServerProcessVersion(this, client, req);
113
+ } else if (path == P_STATIC_STRING("/shutdown.json")) {
114
+ apiServerProcessShutdown(this, client, req);
115
+ } else if (path == P_STATIC_STRING("/gc.json")) {
116
+ processGc(client, req);
117
+ } else if (path == P_STATIC_STRING("/config.json")) {
118
+ processConfig(client, req);
119
+ } else if (path == P_STATIC_STRING("/reinherit_logs.json")) {
120
+ apiServerProcessReinheritLogs(this, client, req,
121
+ instanceDir, fdPassingPassword);
122
+ } else if (path == P_STATIC_STRING("/reopen_logs.json")) {
123
+ apiServerProcessReopenLogs(this, client, req);
124
+ } else {
125
+ apiServerRespondWith404(this, client, req);
126
+ }
127
+ }
128
+
182
129
  void processServerConnectionOperation(Client *client, Request *req) {
183
- if (!authorize(client, req, FULL)) {
184
- respondWith401(client, req);
130
+ if (!authorizeAdminOperation(this, client, req)) {
131
+ apiServerRespondWith401(this, client, req);
185
132
  } else if (req->method == HTTP_DELETE) {
186
133
  StaticString path = req->getPathWithoutQueryString();
187
134
  boost::smatch results;
@@ -193,8 +140,6 @@ private:
193
140
  }
194
141
 
195
142
  int threadNumber = extractThreadNumberFromClientName(results.str(1));
196
- P_WARN(results.str(1));
197
- P_WARN(threadNumber);
198
143
  if (threadNumber < 1 || (unsigned int) threadNumber > requestHandlers.size()) {
199
144
  HeaderTable headers;
200
145
  headers.insert(req->pool, "Content-Type", "application/json");
@@ -217,7 +162,7 @@ private:
217
162
  endRequest(&client, &req);
218
163
  }
219
164
  } else {
220
- respondWith405(client, req);
165
+ apiServerRespondWith405(this, client, req);
221
166
  }
222
167
  }
223
168
 
@@ -226,7 +171,7 @@ private:
226
171
  }
227
172
 
228
173
  void processServerStatus(Client *client, Request *req) {
229
- if (authorize(client, req, READONLY)) {
174
+ if (authorizeStateInspectionOperation(this, client, req)) {
230
175
  HeaderTable headers;
231
176
  headers.insert(req->pool, "Content-Type", "application/json");
232
177
 
@@ -247,93 +192,105 @@ private:
247
192
  endRequest(&client, &req);
248
193
  }
249
194
  } else {
250
- respondWith401(client, req);
195
+ apiServerRespondWith401(this, client, req);
251
196
  }
252
197
  }
253
198
 
254
199
  void processPoolStatusXml(Client *client, Request *req) {
255
- if (authorize(client, req, READONLY)) {
256
- try {
257
- VariantMap params = parseQueryString(req->getQueryString());
258
- HeaderTable headers;
259
- headers.insert(req->pool, "Content-Type", "text/xml");
260
- writeSimpleResponse(client, 200, &headers,
261
- psg_pstrdup(req->pool, appPool->toXml(
262
- params.getBool("secrets", false, false))));
263
- } catch (const SyntaxError &e) {
264
- SKC_ERROR(client, e.what());
265
- }
200
+ Authorization auth(authorize(this, client, req));
201
+ if (auth.canReadPool) {
202
+ ApplicationPool2::Pool::ToXmlOptions options(
203
+ parseQueryString(req->getQueryString()));
204
+ options.uid = auth.uid;
205
+ options.apiKey = auth.apiKey;
206
+
207
+ HeaderTable headers;
208
+ headers.insert(req->pool, "Content-Type", "text/xml");
209
+ writeSimpleResponse(client, 200, &headers,
210
+ psg_pstrdup(req->pool, appPool->toXml(options)));
266
211
  if (!req->ended()) {
267
212
  endRequest(&client, &req);
268
213
  }
269
214
  } else {
270
- respondWith401(client, req);
215
+ apiServerRespondWith401(this, client, req);
271
216
  }
272
217
  }
273
218
 
274
219
  void processPoolStatusTxt(Client *client, Request *req) {
275
- if (authorize(client, req, READONLY)) {
276
- try {
277
- ApplicationPool2::Pool::InspectOptions options(
278
- parseQueryString(req->getQueryString()));
279
- HeaderTable headers;
280
- headers.insert(req->pool, "Content-Type", "text/plain");
281
- writeSimpleResponse(client, 200, &headers,
282
- psg_pstrdup(req->pool, appPool->inspect(options)));
283
- } catch (const SyntaxError &e) {
284
- SKC_ERROR(client, e.what());
285
- }
220
+ Authorization auth(authorize(this, client, req));
221
+ if (auth.canReadPool) {
222
+ ApplicationPool2::Pool::InspectOptions options(
223
+ parseQueryString(req->getQueryString()));
224
+ options.uid = auth.uid;
225
+ options.apiKey = auth.apiKey;
226
+
227
+ HeaderTable headers;
228
+ headers.insert(req->pool, "Content-Type", "text/plain");
229
+ writeSimpleResponse(client, 200, &headers,
230
+ psg_pstrdup(req->pool, appPool->inspect(options)));
286
231
  if (!req->ended()) {
287
232
  endRequest(&client, &req);
288
233
  }
289
234
  } else {
290
- respondWith401(client, req);
235
+ apiServerRespondWith401(this, client, req);
291
236
  }
292
237
  }
293
238
 
294
239
  void processPoolRestartAppGroup(Client *client, Request *req) {
295
- if (req->method != HTTP_POST) {
296
- respondWith405(client, req);
297
- } else if (!authorize(client, req, FULL)) {
298
- respondWith401(client, req);
240
+ Authorization auth(authorize(this, client, req));
241
+ if (!auth.canModifyPool) {
242
+ apiServerRespondWith401(this, client, req);
243
+ } else if (req->method != HTTP_POST) {
244
+ apiServerRespondWith405(this, client, req);
299
245
  } else if (!req->hasBody()) {
300
246
  endAsBadRequest(&client, &req, "Body required");
301
247
  } else if (requestBodyExceedsLimit(client, req)) {
302
- respondWith413(client, req);
248
+ apiServerRespondWith413(this, client, req);
249
+ } else {
250
+ req->authorization = auth;
251
+ // Continues in processPoolRestartAppGroupBody().
303
252
  }
304
- // Continues in processPoolRestartAppGroupBody().
305
253
  }
306
254
 
307
255
  void processPoolRestartAppGroupBody(Client *client, Request *req) {
308
- HeaderTable headers;
309
- RestartMethod method = ApplicationPool2::RM_DEFAULT;
310
-
311
- headers.insert(req->pool, "Content-Type", "application/json");
312
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
313
-
314
256
  if (!req->jsonBody.isMember("name")) {
315
257
  endAsBadRequest(&client, &req, "Name required");
316
258
  return;
317
259
  }
318
260
 
261
+ ApplicationPool2::Pool::RestartOptions options;
262
+ options.uid = req->authorization.uid;
263
+ options.apiKey = req->authorization.apiKey;
319
264
  if (req->jsonBody.isMember("restart_method")) {
320
265
  string restartMethodString = req->jsonBody["restart_method"].asString();
321
266
  if (restartMethodString == "blocking") {
322
- method = RM_BLOCKING;
267
+ options.method = RM_BLOCKING;
323
268
  } else if (restartMethodString == "rolling") {
324
- method = RM_ROLLING;
269
+ options.method = RM_ROLLING;
325
270
  } else {
326
271
  endAsBadRequest(&client, &req, "Unsupported restart method");
327
272
  return;
328
273
  }
329
274
  }
330
275
 
276
+ bool result;
331
277
  const char *response;
332
- if (appPool->restartGroupByName(req->jsonBody["name"].asString(), method)) {
278
+ try {
279
+ result = appPool->restartGroupByName(req->jsonBody["name"].asString(),
280
+ options);
281
+ } catch (const SecurityException &) {
282
+ apiServerRespondWith401(this, client, req);
283
+ return;
284
+ }
285
+ if (result) {
333
286
  response = "{ \"restarted\": true }";
334
287
  } else {
335
288
  response = "{ \"restarted\": false }";
336
289
  }
290
+
291
+ HeaderTable headers;
292
+ headers.insert(req->pool, "Content-Type", "application/json");
293
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
337
294
  writeSimpleResponse(client, 200, &headers, response);
338
295
 
339
296
  if (!req->ended()) {
@@ -342,82 +299,52 @@ private:
342
299
  }
343
300
 
344
301
  void processPoolDetachProcess(Client *client, Request *req) {
345
- if (req->method != HTTP_POST) {
346
- respondWith405(client, req);
347
- } else if (!authorize(client, req, FULL)) {
348
- respondWith401(client, req);
302
+ Authorization auth(authorize(this, client, req));
303
+ if (!auth.canModifyPool) {
304
+ apiServerRespondWith401(this, client, req);
305
+ } else if (req->method != HTTP_POST) {
306
+ apiServerRespondWith405(this, client, req);
349
307
  } else if (!req->hasBody()) {
350
308
  endAsBadRequest(&client, &req, "Body required");
351
309
  } else if (requestBodyExceedsLimit(client, req)) {
352
- respondWith413(client, req);
310
+ apiServerRespondWith413(this, client, req);
311
+ } else {
312
+ req->authorization = auth;
313
+ // Continues in processPoolDetachProcessBody().
353
314
  }
354
- // Continues in processPoolDetachProcessBody().
355
315
  }
356
316
 
357
317
  void processPoolDetachProcessBody(Client *client, Request *req) {
358
- HeaderTable headers;
359
- const char *response;
360
-
361
- headers.insert(req->pool, "Content-Type", "application/json");
362
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
363
-
364
318
  if (req->jsonBody.isMember("pid")) {
365
319
  pid_t pid = (pid_t) req->jsonBody["pid"].asUInt();
366
- if (appPool->detachProcess(pid)) {
320
+ ApplicationPool2::Pool::AuthenticationOptions options;
321
+ options.uid = req->authorization.uid;
322
+ options.apiKey = req->authorization.apiKey;
323
+
324
+ bool result;
325
+ try {
326
+ result = appPool->detachProcess(pid, options);
327
+ } catch (const SecurityException &) {
328
+ apiServerRespondWith401(this, client, req);
329
+ return;
330
+ }
331
+
332
+ const char *response;
333
+ if (result) {
367
334
  response = "{ \"detached\": true }";
368
335
  } else {
369
336
  response = "{ \"detached\": false }";
370
337
  }
371
- writeSimpleResponse(client, 200, &headers, response);
372
- if (!req->ended()) {
373
- endRequest(&client, &req);
374
- }
375
- } else {
376
- endAsBadRequest(&client, &req, "PID required");
377
- }
378
- }
379
338
 
380
- void processBacktraces(Client *client, Request *req) {
381
- if (authorize(client, req, READONLY)) {
382
339
  HeaderTable headers;
383
- headers.insert(req->pool, "Content-Type", "text/plain");
384
- writeSimpleResponse(client, 200, &headers,
385
- psg_pstrdup(req->pool, oxt::thread::all_backtraces()));
386
- if (!req->ended()) {
387
- endRequest(&client, &req);
388
- }
389
- } else {
390
- respondWith401(client, req);
391
- }
392
- }
393
-
394
- void processPing(Client *client, Request *req) {
395
- if (authorize(client, req, READONLY)) {
396
- HeaderTable headers;
397
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
398
340
  headers.insert(req->pool, "Content-Type", "application/json");
399
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
400
- if (!req->ended()) {
401
- endRequest(&client, &req);
402
- }
403
- } else {
404
- respondWith401(client, req);
405
- }
406
- }
407
-
408
- void processShutdown(Client *client, Request *req) {
409
- if (req->method != HTTP_PUT) {
410
- respondWith405(client, req);
411
- } else if (authorize(client, req, FULL)) {
412
- HeaderTable headers;
413
- headers.insert(req->pool, "Content-Type", "application/json");
414
- exitEvent->notify();
415
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
341
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
342
+ writeSimpleResponse(client, 200, &headers, response);
416
343
  if (!req->ended()) {
417
344
  endRequest(&client, &req);
418
345
  }
419
346
  } else {
420
- respondWith401(client, req);
347
+ endAsBadRequest(&client, &req, "PID required");
421
348
  }
422
349
  }
423
350
 
@@ -433,8 +360,8 @@ private:
433
360
 
434
361
  void processGc(Client *client, Request *req) {
435
362
  if (req->method != HTTP_PUT) {
436
- respondWith405(client, req);
437
- } else if (authorize(client, req, FULL)) {
363
+ apiServerRespondWith405(this, client, req);
364
+ } else if (authorizeAdminOperation(this, client, req)) {
438
365
  HeaderTable headers;
439
366
  headers.insert(req->pool, "Content-Type", "application/json");
440
367
  for (unsigned int i = 0; i < requestHandlers.size(); i++) {
@@ -446,7 +373,7 @@ private:
446
373
  endRequest(&client, &req);
447
374
  }
448
375
  } else {
449
- respondWith401(client, req);
376
+ apiServerRespondWith401(this, client, req);
450
377
  }
451
378
  }
452
379
 
@@ -456,8 +383,8 @@ private:
456
383
 
457
384
  void processConfig(Client *client, Request *req) {
458
385
  if (req->method == HTTP_GET) {
459
- if (!authorize(client, req, READONLY)) {
460
- respondWith401(client, req);
386
+ if (!authorizeStateInspectionOperation(this, client, req)) {
387
+ apiServerRespondWith401(this, client, req);
461
388
  }
462
389
 
463
390
  HeaderTable headers;
@@ -482,14 +409,14 @@ private:
482
409
  endRequest(&client, &req);
483
410
  }
484
411
  } else if (req->method == HTTP_PUT) {
485
- if (!authorize(client, req, FULL)) {
486
- respondWith401(client, req);
412
+ if (!authorizeAdminOperation(this, client, req)) {
413
+ apiServerRespondWith401(this, client, req);
487
414
  } else if (!req->hasBody()) {
488
415
  endAsBadRequest(&client, &req, "Body required");
489
416
  }
490
417
  // Continue in processConfigBody()
491
418
  } else {
492
- respondWith405(client, req);
419
+ apiServerRespondWith405(this, client, req);
493
420
  }
494
421
  }
495
422
 
@@ -550,67 +477,6 @@ private:
550
477
  }
551
478
  }
552
479
 
553
- void processReopenLogs(Client *client, Request *req) {
554
- if (req->method != HTTP_POST) {
555
- respondWith405(client, req);
556
- } else if (authorize(client, req, FULL)) {
557
- int e;
558
- HeaderTable headers;
559
- headers.insert(req->pool, "Content-Type", "application/json");
560
-
561
- string logFile = getLogFile();
562
- if (logFile.empty()) {
563
- writeSimpleResponse(client, 500, &headers, "{ \"status\": \"error\", "
564
- "\"code\": \"NO_LOG_FILE\", "
565
- "\"message\": \"" PROGRAM_NAME " was not configured with a log file.\" }\n");
566
- if (!req->ended()) {
567
- endRequest(&client, &req);
568
- }
569
- return;
570
- }
571
-
572
- if (!setLogFile(logFile, &e)) {
573
- unsigned int bufsize = 1024;
574
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
575
- snprintf(message, bufsize, "{ \"status\": \"error\", "
576
- "\"code\": \"LOG_FILE_OPEN_ERROR\", "
577
- "\"message\": \"Cannot reopen log file %s: %s (errno=%d)\" }",
578
- logFile.c_str(), strerror(e), e);
579
- writeSimpleResponse(client, 500, &headers, message);
580
- if (!req->ended()) {
581
- endRequest(&client, &req);
582
- }
583
- return;
584
- }
585
- P_NOTICE("Log file reopened.");
586
-
587
- if (hasFileDescriptorLogFile()) {
588
- if (!setFileDescriptorLogFile(getFileDescriptorLogFile(), &e)) {
589
- unsigned int bufsize = 1024;
590
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
591
- snprintf(message, bufsize, "{ \"status\": \"error\", "
592
- "\"code\": \"FD_LOG_FILE_OPEN_ERROR\", "
593
- "\"message\": \"Cannot reopen file descriptor log file %s: %s (errno=%d)\" }",
594
- getFileDescriptorLogFile().c_str(), strerror(e), e);
595
- writeSimpleResponse(client, 500, &headers, message);
596
- if (!req->ended()) {
597
- endRequest(&client, &req);
598
- }
599
- return;
600
- }
601
- P_NOTICE("File descriptor log file reopened.");
602
- }
603
-
604
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }\n");
605
-
606
- if (!req->ended()) {
607
- endRequest(&client, &req);
608
- }
609
- } else {
610
- respondWith401(client, req);
611
- }
612
- }
613
-
614
480
  bool requestBodyExceedsLimit(Client *client, Request *req, unsigned int limit = 1024 * 128) {
615
481
  return (req->bodyType == Request::RBT_CONTENT_LENGTH
616
482
  && req->aux.bodyInfo.contentLength > limit)
@@ -618,92 +484,20 @@ private:
618
484
  && req->body.size() > limit);
619
485
  }
620
486
 
621
- void respondWith401(Client *client, Request *req) {
622
- HeaderTable headers;
623
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
624
- headers.insert(req->pool, "WWW-Authenticate", "Basic realm=\"admin\"");
625
- writeSimpleResponse(client, 401, &headers, "Unauthorized");
626
- if (!req->ended()) {
627
- endRequest(&client, &req);
628
- }
629
- }
630
-
631
- void respondWith404(Client *client, Request *req) {
632
- HeaderTable headers;
633
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
634
- writeSimpleResponse(client, 404, &headers, "Not found");
635
- if (!req->ended()) {
636
- endRequest(&client, &req);
637
- }
638
- }
639
-
640
- void respondWith405(Client *client, Request *req) {
641
- HeaderTable headers;
642
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
643
- writeSimpleResponse(client, 405, &headers, "Method not allowed");
644
- if (!req->ended()) {
645
- endRequest(&client, &req);
646
- }
647
- }
648
-
649
- void respondWith413(Client *client, Request *req) {
650
- HeaderTable headers;
651
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
652
- writeSimpleResponse(client, 413, &headers, "Request body too large");
653
- if (!req->ended()) {
654
- endRequest(&client, &req);
655
- }
656
- }
657
-
658
- void respondWith422(Client *client, Request *req, const StaticString &body) {
659
- HeaderTable headers;
660
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
661
- headers.insert(req->pool, "Content-Type", "text/plain; charset=utf-8");
662
- writeSimpleResponse(client, 422, &headers, body);
663
- if (!req->ended()) {
664
- endRequest(&client, &req);
665
- }
666
- }
667
-
668
487
  protected:
669
488
  virtual void onRequestBegin(Client *client, Request *req) {
670
489
  TRACE_POINT();
671
490
  StaticString path = req->getPathWithoutQueryString();
672
491
 
673
- P_INFO("Admin request: " << http_method_str(req->method) <<
492
+ P_INFO("API request: " << http_method_str(req->method) <<
674
493
  " " << StaticString(req->path.start->data, req->path.size));
675
494
 
676
495
  try {
677
- if (path == P_STATIC_STRING("/server.json")) {
678
- processServerStatus(client, req);
679
- } else if (regex_match(path, serverConnectionPath)) {
680
- processServerConnectionOperation(client, req);
681
- } else if (path == P_STATIC_STRING("/pool.xml")) {
682
- processPoolStatusXml(client, req);
683
- } else if (path == P_STATIC_STRING("/pool.txt")) {
684
- processPoolStatusTxt(client, req);
685
- } else if (path == P_STATIC_STRING("/pool/restart_app_group.json")) {
686
- processPoolRestartAppGroup(client, req);
687
- } else if (path == P_STATIC_STRING("/pool/detach_process.json")) {
688
- processPoolDetachProcess(client, req);
689
- } else if (path == P_STATIC_STRING("/backtraces.txt")) {
690
- processBacktraces(client, req);
691
- } else if (path == P_STATIC_STRING("/ping.json")) {
692
- processPing(client, req);
693
- } else if (path == P_STATIC_STRING("/shutdown.json")) {
694
- processShutdown(client, req);
695
- } else if (path == P_STATIC_STRING("/gc.json")) {
696
- processGc(client, req);
697
- } else if (path == P_STATIC_STRING("/config.json")) {
698
- processConfig(client, req);
699
- } else if (path == P_STATIC_STRING("/reopen_logs.json")) {
700
- processReopenLogs(client, req);
701
- } else {
702
- respondWith404(client, req);
703
- }
496
+ route(client, req, path);
704
497
  } catch (const oxt::tracable_exception &e) {
705
498
  SKC_ERROR(client, "Exception: " << e.what() << "\n" << e.backtrace());
706
499
  if (!req->ended()) {
500
+ req->wantKeepAlive = false;
707
501
  endRequest(&client, &req);
708
502
  }
709
503
  }
@@ -717,7 +511,7 @@ protected:
717
511
  // Data
718
512
  req->body.append(buffer.start, buffer.size());
719
513
  if (requestBodyExceedsLimit(client, req)) {
720
- respondWith413(client, req);
514
+ apiServerRespondWith413(this, client, req);
721
515
  }
722
516
  } else if (errcode == 0) {
723
517
  // EOF
@@ -737,11 +531,12 @@ protected:
737
531
  } catch (const oxt::tracable_exception &e) {
738
532
  SKC_ERROR(client, "Exception: " << e.what() << "\n" << e.backtrace());
739
533
  if (!req->ended()) {
534
+ req->wantKeepAlive = false;
740
535
  endRequest(&client, &req);
741
536
  }
742
537
  }
743
538
  } else {
744
- respondWith422(client, req, reader.getFormattedErrorMessages());
539
+ apiServerRespondWith422(this, client, req, reader.getFormattedErrorMessages());
745
540
  }
746
541
  } else {
747
542
  // Error
@@ -755,42 +550,45 @@ protected:
755
550
  if (!req->jsonBody.isNull()) {
756
551
  req->jsonBody = Json::Value();
757
552
  }
553
+ req->authorization = Authorization();
758
554
  ParentClass::deinitializeRequest(client, req);
759
555
  }
760
556
 
761
- virtual unsigned int getClientName(const Client *client, char *buf, size_t size) const {
762
- char *pos = buf;
763
- const char *end = buf + size - 1;
764
- pos = appendData(pos, end, "Adm.", 1);
765
- pos += uintToString(client->number, pos, end - pos);
766
- *pos = '\0';
767
- return pos - buf;
768
- }
769
-
770
557
  public:
771
558
  vector<RequestHandler *> requestHandlers;
559
+ ApiAccountDatabase *apiAccountDatabase;
772
560
  ApplicationPool2::PoolPtr appPool;
561
+ string instanceDir;
562
+ string fdPassingPassword;
773
563
  EventFd *exitEvent;
774
564
  vector<Authorization> authorizations;
775
565
 
776
- AdminServer(ServerKit::Context *context)
566
+ ApiServer(ServerKit::Context *context)
777
567
  : ParentClass(context),
778
568
  serverConnectionPath("^/server/(.+)\\.json$"),
569
+ apiAccountDatabase(NULL),
779
570
  exitEvent(NULL)
780
571
  { }
781
572
 
782
573
  virtual StaticString getServerName() const {
783
- return P_STATIC_STRING("AdminServer");
574
+ return P_STATIC_STRING("ApiServer");
784
575
  }
785
576
 
786
- static PrivilegeLevel parseLevel(const StaticString &level) {
787
- if (level == "readonly") {
788
- return READONLY;
789
- } else if (level == "full") {
790
- return FULL;
791
- } else {
792
- throw RuntimeException("Invalid privilege level " + level);
793
- }
577
+ virtual unsigned int getClientName(const Client *client, char *buf, size_t size) const {
578
+ char *pos = buf;
579
+ const char *end = buf + size - 1;
580
+ pos = appendData(pos, end, "Adm.", 1);
581
+ pos += uintToString(client->number, pos, end - pos);
582
+ *pos = '\0';
583
+ return pos - buf;
584
+ }
585
+
586
+ bool authorizeByUid(uid_t uid) const {
587
+ return appPool->authorizeByUid(uid);
588
+ }
589
+
590
+ bool authorizeByApiKey(const ApplicationPool2::ApiKey &apiKey) const {
591
+ return appPool->authorizeByApiKey(apiKey);
794
592
  }
795
593
  };
796
594
 
@@ -798,4 +596,4 @@ public:
798
596
  } // namespace ServerAgent
799
597
  } // namespace Passenger
800
598
 
801
- #endif /* _PASSENGER_SERVER_AGENT_ADMIN_SERVER_H_ */
599
+ #endif /* _PASSENGER_SERVER_AGENT_API_SERVER_H_ */