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
@@ -1,435 +0,0 @@
1
- /*
2
- * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2013-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
- #ifndef _PASSENGER_LOGGING_AGENT_ADMIN_SERVER_H_
26
- #define _PASSENGER_LOGGING_AGENT_ADMIN_SERVER_H_
27
-
28
- #include <sstream>
29
- #include <string>
30
-
31
- #include <agents/LoggingAgent/LoggingServer.h>
32
- #include <ServerKit/HttpServer.h>
33
- #include <DataStructures/LString.h>
34
- #include <Exceptions.h>
35
- #include <StaticString.h>
36
- #include <Utils/StrIntUtils.h>
37
- #include <Utils/modp_b64.h>
38
- #include <Utils/json.h>
39
-
40
- namespace Passenger {
41
- namespace LoggingAgent {
42
-
43
- using namespace std;
44
-
45
-
46
- class Request: public ServerKit::BaseHttpRequest {
47
- public:
48
- string body;
49
- Json::Value jsonBody;
50
-
51
- DEFINE_SERVER_KIT_BASE_HTTP_REQUEST_FOOTER(Request);
52
- };
53
-
54
- class AdminServer: public ServerKit::HttpServer<AdminServer, ServerKit::HttpClient<Request> > {
55
- public:
56
- enum PrivilegeLevel {
57
- NONE,
58
- READONLY,
59
- FULL
60
- };
61
-
62
- struct Authorization {
63
- PrivilegeLevel level;
64
- string username;
65
- string password;
66
- };
67
-
68
- private:
69
- typedef ServerKit::HttpServer<AdminServer, ServerKit::HttpClient<Request> > ParentClass;
70
- typedef ServerKit::HttpClient<Request> Client;
71
- typedef ServerKit::HeaderTable HeaderTable;
72
-
73
- bool parseAuthorizationHeader(Request *req, string &username,
74
- string &password) const
75
- {
76
- const LString *auth = req->headers.lookup("authorization");
77
-
78
- if (auth == NULL || auth->size <= 6 || !psg_lstr_cmp(auth, "Basic ", 6)) {
79
- return false;
80
- }
81
-
82
- auth = psg_lstr_make_contiguous(auth, req->pool);
83
- string authData = modp::b64_decode(
84
- auth->start->data + sizeof("Basic ") - 1,
85
- auth->size - (sizeof("Basic ") - 1));
86
- string::size_type pos = authData.find(':');
87
- if (pos == string::npos) {
88
- return false;
89
- }
90
-
91
- username = authData.substr(0, pos);
92
- password = authData.substr(pos + 1);
93
- return true;
94
- }
95
-
96
- const Authorization *lookupAuthorizationRecord(const StaticString &username) const {
97
- vector<Authorization>::const_iterator it, end = authorizations.end();
98
-
99
- for (it = authorizations.begin(); it != end; it++) {
100
- if (it->username == username) {
101
- return &(*it);
102
- }
103
- }
104
-
105
- return NULL;
106
- }
107
-
108
- bool authorize(Client *client, Request *req, PrivilegeLevel level) const {
109
- if (authorizations.empty()) {
110
- return true;
111
- }
112
-
113
- string username, password;
114
- if (!parseAuthorizationHeader(req, username, password)) {
115
- return false;
116
- }
117
-
118
- const Authorization *auth = lookupAuthorizationRecord(username);
119
- return auth != NULL
120
- && auth->level >= level
121
- && constantTimeCompare(password, auth->password);
122
- }
123
-
124
- void processPing(Client *client, Request *req) {
125
- if (authorize(client, req, READONLY)) {
126
- HeaderTable headers;
127
- headers.insert(req->pool, "cache-control", "no-cache, no-store, must-revalidate");
128
- headers.insert(req->pool, "Content-Type", "application/json");
129
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
130
- if (!req->ended()) {
131
- endRequest(&client, &req);
132
- }
133
- } else {
134
- respondWith401(client, req);
135
- }
136
- }
137
-
138
- void processShutdown(Client *client, Request *req) {
139
- if (req->method != HTTP_POST) {
140
- respondWith405(client, req);
141
- } else if (authorize(client, req, FULL)) {
142
- HeaderTable headers;
143
- headers.insert(req->pool, "Content-Type", "application/json");
144
- exitEvent->notify();
145
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
146
- if (!req->ended()) {
147
- endRequest(&client, &req);
148
- }
149
- } else {
150
- respondWith401(client, req);
151
- }
152
- }
153
-
154
- void processConfig(Client *client, Request *req) {
155
- if (req->method == HTTP_GET) {
156
- if (!authorize(client, req, READONLY)) {
157
- respondWith401(client, req);
158
- }
159
-
160
- HeaderTable headers;
161
- Json::Value doc;
162
- string logFile = getLogFile();
163
- string fileDescriptorLogFile = getFileDescriptorLogFile();
164
-
165
- headers.insert(req->pool, "Content-Type", "application/json");
166
- doc["log_level"] = getLogLevel();
167
- if (!logFile.empty()) {
168
- doc["log_file"] = logFile;
169
- }
170
- if (!fileDescriptorLogFile.empty()) {
171
- doc["file_descriptor_log_file"] = fileDescriptorLogFile;
172
- }
173
-
174
- writeSimpleResponse(client, 200, &headers, doc.toStyledString());
175
- if (!req->ended()) {
176
- endRequest(&client, &req);
177
- }
178
- } else if (req->method == HTTP_PUT) {
179
- if (!authorize(client, req, FULL)) {
180
- respondWith401(client, req);
181
- } else if (!req->hasBody()) {
182
- endAsBadRequest(&client, &req, "Body required");
183
- }
184
- // Continue in processConfigBody()
185
- } else {
186
- respondWith405(client, req);
187
- }
188
- }
189
-
190
- void processConfigBody(Client *client, Request *req) {
191
- HeaderTable headers;
192
- Json::Value &json = req->jsonBody;
193
-
194
- headers.insert(req->pool, "Content-Type", "application/json");
195
-
196
- if (json.isMember("log_level")) {
197
- setLogLevel(json["log_level"].asInt());
198
- }
199
- if (json.isMember("log_file")) {
200
- string logFile = json["log_file"].asString();
201
- try {
202
- logFile = absolutizePath(logFile);
203
- } catch (const SystemException &e) {
204
- unsigned int bufsize = 1024;
205
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
206
- snprintf(message, bufsize, "{ \"status\": \"error\", "
207
- "\"message\": \"Cannot absolutize log file filename: %s\" }",
208
- e.what());
209
- writeSimpleResponse(client, 500, &headers, message);
210
- if (!req->ended()) {
211
- endRequest(&client, &req);
212
- }
213
- return;
214
- }
215
-
216
- int e;
217
- if (!setLogFile(logFile, &e)) {
218
- unsigned int bufsize = 1024;
219
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
220
- snprintf(message, bufsize, "{ \"status\": \"error\", "
221
- "\"message\": \"Cannot open log file: %s (errno=%d)\" }",
222
- strerror(e), e);
223
- writeSimpleResponse(client, 500, &headers, message);
224
- if (!req->ended()) {
225
- endRequest(&client, &req);
226
- }
227
- return;
228
- }
229
- P_NOTICE("Log file opened.");
230
- }
231
-
232
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }\n");
233
- if (!req->ended()) {
234
- endRequest(&client, &req);
235
- }
236
- }
237
-
238
- void processReopenLogs(Client *client, Request *req) {
239
- if (req->method != HTTP_POST) {
240
- respondWith405(client, req);
241
- } else if (authorize(client, req, FULL)) {
242
- int e;
243
- HeaderTable headers;
244
- headers.insert(req->pool, "Content-Type", "application/json");
245
-
246
- string logFile = getLogFile();
247
- if (logFile.empty()) {
248
- writeSimpleResponse(client, 500, &headers, "{ \"status\": \"error\", "
249
- "\"code\": \"NO_LOG_FILE\", "
250
- "\"message\": \"" PROGRAM_NAME " was not configured with a log file.\" }\n");
251
- if (!req->ended()) {
252
- endRequest(&client, &req);
253
- }
254
- return;
255
- }
256
-
257
- if (!setLogFile(logFile, &e)) {
258
- unsigned int bufsize = 1024;
259
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
260
- snprintf(message, bufsize, "{ \"status\": \"error\", "
261
- "\"code\": \"LOG_FILE_OPEN_ERROR\", "
262
- "\"message\": \"Cannot reopen log file %s: %s (errno=%d)\" }",
263
- logFile.c_str(), strerror(e), e);
264
- writeSimpleResponse(client, 500, &headers, message);
265
- if (!req->ended()) {
266
- endRequest(&client, &req);
267
- }
268
- return;
269
- }
270
- P_NOTICE("Log file reopened.");
271
-
272
- if (hasFileDescriptorLogFile()) {
273
- if (!setFileDescriptorLogFile(getFileDescriptorLogFile(), &e)) {
274
- unsigned int bufsize = 1024;
275
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
276
- snprintf(message, bufsize, "{ \"status\": \"error\", "
277
- "\"code\": \"FD_LOG_FILE_OPEN_ERROR\", "
278
- "\"message\": \"Cannot reopen file descriptor log file %s: %s (errno=%d)\" }",
279
- getFileDescriptorLogFile().c_str(), strerror(e), e);
280
- writeSimpleResponse(client, 500, &headers, message);
281
- if (!req->ended()) {
282
- endRequest(&client, &req);
283
- }
284
- return;
285
- }
286
- P_NOTICE("File descriptor log file reopened.");
287
- }
288
-
289
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }\n");
290
-
291
- if (!req->ended()) {
292
- endRequest(&client, &req);
293
- }
294
- } else {
295
- respondWith401(client, req);
296
- }
297
- }
298
-
299
- void processStatusTxt(Client *client, Request *req) {
300
- if (req->method != HTTP_GET) {
301
- respondWith405(client, req);
302
- } else if (authorize(client, req, READONLY)) {
303
- HeaderTable headers;
304
- headers.insert(req->pool, "Content-Type", "text/plain");
305
-
306
- stringstream stream;
307
- loggingServer->dump(stream);
308
- writeSimpleResponse(client, 200, &headers, stream.str());
309
- if (!req->ended()) {
310
- endRequest(&client, &req);
311
- }
312
- } else {
313
- respondWith401(client, req);
314
- }
315
- }
316
-
317
- void respondWith401(Client *client, Request *req) {
318
- HeaderTable headers;
319
- headers.insert(req->pool, "cache-control", "no-cache, no-store, must-revalidate");
320
- headers.insert(req->pool, "WWW-Authenticate", "Basic realm=\"admin\"");
321
- writeSimpleResponse(client, 401, &headers, "Unauthorized");
322
- if (!req->ended()) {
323
- endRequest(&client, &req);
324
- }
325
- }
326
-
327
- void respondWith404(Client *client, Request *req) {
328
- HeaderTable headers;
329
- headers.insert(req->pool, "cache-control", "no-cache, no-store, must-revalidate");
330
- writeSimpleResponse(client, 404, &headers, "Not found");
331
- if (!req->ended()) {
332
- endRequest(&client, &req);
333
- }
334
- }
335
-
336
- void respondWith405(Client *client, Request *req) {
337
- HeaderTable headers;
338
- headers.insert(req->pool, "cache-control", "no-cache, no-store, must-revalidate");
339
- writeSimpleResponse(client, 405, &headers, "Method not allowed");
340
- if (!req->ended()) {
341
- endRequest(&client, &req);
342
- }
343
- }
344
-
345
- void respondWith422(Client *client, Request *req, const StaticString &body) {
346
- HeaderTable headers;
347
- headers.insert(req->pool, "cache-control", "no-cache, no-store, must-revalidate");
348
- headers.insert(req->pool, "Content-Type", "text/plain; charset=utf-8");
349
- writeSimpleResponse(client, 422, &headers, body);
350
- if (!req->ended()) {
351
- endRequest(&client, &req);
352
- }
353
- }
354
-
355
- protected:
356
- virtual void onRequestBegin(Client *client, Request *req) {
357
- const StaticString path(req->path.start->data, req->path.size);
358
-
359
- P_INFO("Admin request: " << path);
360
-
361
- if (path == P_STATIC_STRING("/ping.json")) {
362
- processPing(client, req);
363
- } else if (path == P_STATIC_STRING("/shutdown.json")) {
364
- processShutdown(client, req);
365
- } else if (path == P_STATIC_STRING("/config.json")) {
366
- processConfig(client, req);
367
- } else if (path == P_STATIC_STRING("/reopen_logs.json")) {
368
- processReopenLogs(client, req);
369
- } else if (path == P_STATIC_STRING("/status.txt")) {
370
- processStatusTxt(client, req);
371
- } else {
372
- respondWith404(client, req);
373
- }
374
- }
375
-
376
- virtual ServerKit::Channel::Result onRequestBody(Client *client, Request *req,
377
- const MemoryKit::mbuf &buffer, int errcode)
378
- {
379
- if (buffer.size() > 0) {
380
- // Data
381
- req->body.append(buffer.start, buffer.size());
382
- } else if (errcode == 0) {
383
- // EOF
384
- Json::Reader reader;
385
- if (reader.parse(req->body, req->jsonBody)) {
386
- processConfigBody(client, req);
387
- } else {
388
- respondWith422(client, req, reader.getFormattedErrorMessages());
389
- }
390
- } else {
391
- // Error
392
- disconnect(&client);
393
- }
394
- return ServerKit::Channel::Result(buffer.size(), false);
395
- }
396
-
397
- virtual void deinitializeRequest(Client *client, Request *req) {
398
- req->body.clear();
399
- if (!req->jsonBody.isNull()) {
400
- req->jsonBody = Json::Value();
401
- }
402
- ParentClass::deinitializeRequest(client, req);
403
- }
404
-
405
- public:
406
- LoggingServer *loggingServer;
407
- EventFd *exitEvent;
408
- vector<Authorization> authorizations;
409
-
410
- AdminServer(ServerKit::Context *context)
411
- : ParentClass(context),
412
- loggingServer(NULL),
413
- exitEvent(NULL)
414
- { }
415
-
416
- virtual StaticString getServerName() const {
417
- return P_STATIC_STRING("LoggerAdminServer");
418
- }
419
-
420
- static PrivilegeLevel parseLevel(const StaticString &level) {
421
- if (level == "readonly") {
422
- return READONLY;
423
- } else if (level == "full") {
424
- return FULL;
425
- } else {
426
- throw RuntimeException("Invalid privilege level " + level);
427
- }
428
- }
429
- };
430
-
431
-
432
- } // namespace LoggingAgent
433
- } // namespace Passenger
434
-
435
- #endif /* _PASSENGER_LOGGING_AGENT_ADMIN_SERVER_H_ */
@@ -1,432 +0,0 @@
1
- /*
2
- * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2014-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
- #ifndef _PASSENGER_WATCHDOG_AGENT_ADMIN_SERVER_H_
26
- #define _PASSENGER_WATCHDOG_AGENT_ADMIN_SERVER_H_
27
-
28
- #include <sstream>
29
- #include <string>
30
- #include <cstring>
31
-
32
- #include <ServerKit/HttpServer.h>
33
- #include <DataStructures/LString.h>
34
- #include <Exceptions.h>
35
- #include <StaticString.h>
36
- #include <Logging.h>
37
- #include <Constants.h>
38
- #include <Utils/StrIntUtils.h>
39
- #include <Utils/modp_b64.h>
40
- #include <Utils/json.h>
41
-
42
- namespace Passenger {
43
- namespace WatchdogAgent {
44
-
45
- using namespace std;
46
-
47
-
48
- class Request: public ServerKit::BaseHttpRequest {
49
- public:
50
- string body;
51
- Json::Value jsonBody;
52
-
53
- DEFINE_SERVER_KIT_BASE_HTTP_REQUEST_FOOTER(Request);
54
- };
55
-
56
- class AdminServer: public ServerKit::HttpServer<AdminServer, ServerKit::HttpClient<Request> > {
57
- public:
58
- enum PrivilegeLevel {
59
- NONE,
60
- READONLY,
61
- FULL
62
- };
63
-
64
- struct Authorization {
65
- PrivilegeLevel level;
66
- string username;
67
- string password;
68
- };
69
-
70
- private:
71
- typedef ServerKit::HttpServer<AdminServer, ServerKit::HttpClient<Request> > ParentClass;
72
- typedef ServerKit::HttpClient<Request> Client;
73
- typedef ServerKit::HeaderTable HeaderTable;
74
-
75
- bool parseAuthorizationHeader(Request *req, string &username,
76
- string &password) const
77
- {
78
- const LString *auth = req->headers.lookup("authorization");
79
-
80
- if (auth == NULL || auth->size <= 6 || !psg_lstr_cmp(auth, "Basic ", 6)) {
81
- return false;
82
- }
83
-
84
- auth = psg_lstr_make_contiguous(auth, req->pool);
85
- string authData = modp::b64_decode(
86
- auth->start->data + sizeof("Basic ") - 1,
87
- auth->size - (sizeof("Basic ") - 1));
88
- string::size_type pos = authData.find(':');
89
- if (pos == string::npos) {
90
- return false;
91
- }
92
-
93
- username = authData.substr(0, pos);
94
- password = authData.substr(pos + 1);
95
- return true;
96
- }
97
-
98
- const Authorization *lookupAuthorizationRecord(const StaticString &username) const {
99
- vector<Authorization>::const_iterator it, end = authorizations.end();
100
-
101
- for (it = authorizations.begin(); it != end; it++) {
102
- if (it->username == username) {
103
- return &(*it);
104
- }
105
- }
106
-
107
- return NULL;
108
- }
109
-
110
- bool authorize(Client *client, Request *req, PrivilegeLevel level) const {
111
- if (authorizations.empty()) {
112
- return true;
113
- }
114
-
115
- string username, password;
116
- if (!parseAuthorizationHeader(req, username, password)) {
117
- return false;
118
- }
119
-
120
- const Authorization *auth = lookupAuthorizationRecord(username);
121
- return auth != NULL
122
- && auth->level >= level
123
- && constantTimeCompare(password, auth->password);
124
- }
125
-
126
- void processStatusTxt(Client *client, Request *req) {
127
- if (authorize(client, req, READONLY)) {
128
- HeaderTable headers;
129
- //stringstream stream;
130
- headers.insert(req->pool, "Content-Type", "text/plain");
131
- //loggingServer->dump(stream);
132
- //writeSimpleResponse(client, 200, &headers, stream.str());
133
- if (!req->ended()) {
134
- endRequest(&client, &req);
135
- }
136
- } else {
137
- respondWith401(client, req);
138
- }
139
- }
140
-
141
- void processPing(Client *client, Request *req) {
142
- if (authorize(client, req, READONLY)) {
143
- HeaderTable headers;
144
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
145
- headers.insert(req->pool, "Content-Type", "application/json");
146
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
147
- if (!req->ended()) {
148
- endRequest(&client, &req);
149
- }
150
- } else {
151
- respondWith401(client, req);
152
- }
153
- }
154
-
155
- void processShutdown(Client *client, Request *req) {
156
- if (req->method != HTTP_POST) {
157
- respondWith405(client, req);
158
- } else if (authorize(client, req, FULL)) {
159
- HeaderTable headers;
160
- headers.insert(req->pool, "Content-Type", "application/json");
161
- exitEvent->notify();
162
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
163
- if (!req->ended()) {
164
- endRequest(&client, &req);
165
- }
166
- } else {
167
- respondWith401(client, req);
168
- }
169
- }
170
-
171
- void processConfig(Client *client, Request *req) {
172
- if (req->method == HTTP_GET) {
173
- if (!authorize(client, req, READONLY)) {
174
- respondWith401(client, req);
175
- }
176
-
177
- HeaderTable headers;
178
- Json::Value doc;
179
- string logFile = getLogFile();
180
- string fileDescriptorLogFile = getFileDescriptorLogFile();
181
-
182
- headers.insert(req->pool, "Content-Type", "application/json");
183
- doc["log_level"] = getLogLevel();
184
- if (!logFile.empty()) {
185
- doc["log_file"] = logFile;
186
- }
187
- if (!fileDescriptorLogFile.empty()) {
188
- doc["file_descriptor_log_file"] = fileDescriptorLogFile;
189
- }
190
-
191
- writeSimpleResponse(client, 200, &headers, doc.toStyledString());
192
- if (!req->ended()) {
193
- endRequest(&client, &req);
194
- }
195
- } else if (req->method == HTTP_PUT) {
196
- if (!authorize(client, req, FULL)) {
197
- respondWith401(client, req);
198
- } else if (!req->hasBody()) {
199
- endAsBadRequest(&client, &req, "Body required");
200
- }
201
- // Continue in processConfigBody()
202
- } else {
203
- respondWith405(client, req);
204
- }
205
- }
206
-
207
- void processConfigBody(Client *client, Request *req) {
208
- HeaderTable headers;
209
- Json::Value &json = req->jsonBody;
210
-
211
- headers.insert(req->pool, "Content-Type", "application/json");
212
-
213
- if (json.isMember("log_level")) {
214
- setLogLevel(json["log_level"].asInt());
215
- }
216
- if (json.isMember("log_file")) {
217
- string logFile = json["log_file"].asString();
218
- try {
219
- logFile = absolutizePath(logFile);
220
- } catch (const SystemException &e) {
221
- unsigned int bufsize = 1024;
222
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
223
- snprintf(message, bufsize, "{ \"status\": \"error\", "
224
- "\"message\": \"Cannot absolutize log file filename: %s\" }",
225
- e.what());
226
- writeSimpleResponse(client, 500, &headers, message);
227
- if (!req->ended()) {
228
- endRequest(&client, &req);
229
- }
230
- return;
231
- }
232
-
233
- int e;
234
- if (!setLogFile(logFile, &e)) {
235
- unsigned int bufsize = 1024;
236
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
237
- snprintf(message, bufsize, "{ \"status\": \"error\", "
238
- "\"message\": \"Cannot open log file: %s (errno=%d)\" }",
239
- strerror(e), e);
240
- writeSimpleResponse(client, 500, &headers, message);
241
- if (!req->ended()) {
242
- endRequest(&client, &req);
243
- }
244
- return;
245
- }
246
- P_NOTICE("Log file opened.");
247
- }
248
-
249
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
250
- if (!req->ended()) {
251
- endRequest(&client, &req);
252
- }
253
- }
254
-
255
- void processReopenLogs(Client *client, Request *req) {
256
- if (req->method != HTTP_POST) {
257
- respondWith405(client, req);
258
- } else if (authorize(client, req, FULL)) {
259
- int e;
260
- HeaderTable headers;
261
- headers.insert(req->pool, "Content-Type", "application/json");
262
-
263
- string logFile = getLogFile();
264
- if (logFile.empty()) {
265
- writeSimpleResponse(client, 500, &headers, "{ \"status\": \"error\", "
266
- "\"code\": \"NO_LOG_FILE\", "
267
- "\"message\": \"" PROGRAM_NAME " was not configured with a log file.\" }\n");
268
- if (!req->ended()) {
269
- endRequest(&client, &req);
270
- }
271
- return;
272
- }
273
-
274
- if (!setLogFile(logFile, &e)) {
275
- unsigned int bufsize = 1024;
276
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
277
- snprintf(message, bufsize, "{ \"status\": \"error\", "
278
- "\"code\": \"LOG_FILE_OPEN_ERROR\", "
279
- "\"message\": \"Cannot reopen log file %s: %s (errno=%d)\" }",
280
- logFile.c_str(), strerror(e), e);
281
- writeSimpleResponse(client, 500, &headers, message);
282
- if (!req->ended()) {
283
- endRequest(&client, &req);
284
- }
285
- return;
286
- }
287
- P_NOTICE("Log file reopened.");
288
-
289
- if (hasFileDescriptorLogFile()) {
290
- if (!setFileDescriptorLogFile(getFileDescriptorLogFile(), &e)) {
291
- unsigned int bufsize = 1024;
292
- char *message = (char *) psg_pnalloc(req->pool, bufsize);
293
- snprintf(message, bufsize, "{ \"status\": \"error\", "
294
- "\"code\": \"FD_LOG_FILE_OPEN_ERROR\", "
295
- "\"message\": \"Cannot reopen file descriptor log file %s: %s (errno=%d)\" }",
296
- getFileDescriptorLogFile().c_str(), strerror(e), e);
297
- writeSimpleResponse(client, 500, &headers, message);
298
- if (!req->ended()) {
299
- endRequest(&client, &req);
300
- }
301
- return;
302
- }
303
- P_NOTICE("File descriptor log file reopened.");
304
- }
305
-
306
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }\n");
307
-
308
- if (!req->ended()) {
309
- endRequest(&client, &req);
310
- }
311
- } else {
312
- respondWith401(client, req);
313
- }
314
- }
315
-
316
- void respondWith401(Client *client, Request *req) {
317
- HeaderTable headers;
318
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
319
- headers.insert(req->pool, "WWW-Authenticate", "Basic realm=\"admin\"");
320
- writeSimpleResponse(client, 401, &headers, "Unauthorized");
321
- if (!req->ended()) {
322
- endRequest(&client, &req);
323
- }
324
- }
325
-
326
- void respondWith404(Client *client, Request *req) {
327
- HeaderTable headers;
328
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
329
- writeSimpleResponse(client, 404, &headers, "Not found");
330
- if (!req->ended()) {
331
- endRequest(&client, &req);
332
- }
333
- }
334
-
335
- void respondWith405(Client *client, Request *req) {
336
- HeaderTable headers;
337
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
338
- writeSimpleResponse(client, 405, &headers, "Method not allowed");
339
- if (!req->ended()) {
340
- endRequest(&client, &req);
341
- }
342
- }
343
-
344
- void respondWith422(Client *client, Request *req, const StaticString &body) {
345
- HeaderTable headers;
346
- headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
347
- headers.insert(req->pool, "Content-Type", "text/plain; charset=utf-8");
348
- writeSimpleResponse(client, 422, &headers, body);
349
- if (!req->ended()) {
350
- endRequest(&client, &req);
351
- }
352
- }
353
-
354
- protected:
355
- virtual void onRequestBegin(Client *client, Request *req) {
356
- const StaticString path(req->path.start->data, req->path.size);
357
-
358
- P_INFO("Admin request: " << path);
359
-
360
- if (path == P_STATIC_STRING("/status.txt")) {
361
- processStatusTxt(client, req);
362
- } else if (path == P_STATIC_STRING("/ping.json")) {
363
- processPing(client, req);
364
- } else if (path == P_STATIC_STRING("/shutdown.json")) {
365
- processShutdown(client, req);
366
- } else if (path == P_STATIC_STRING("/config.json")) {
367
- processConfig(client, req);
368
- } else if (path == P_STATIC_STRING("/reopen_logs.json")) {
369
- processReopenLogs(client, req);
370
- } else {
371
- respondWith404(client, req);
372
- }
373
- }
374
-
375
- virtual ServerKit::Channel::Result onRequestBody(Client *client, Request *req,
376
- const MemoryKit::mbuf &buffer, int errcode)
377
- {
378
- if (buffer.size() > 0) {
379
- // Data
380
- req->body.append(buffer.start, buffer.size());
381
- } else if (errcode == 0) {
382
- // EOF
383
- Json::Reader reader;
384
- if (reader.parse(req->body, req->jsonBody)) {
385
- processConfigBody(client, req);
386
- } else {
387
- respondWith422(client, req, reader.getFormattedErrorMessages());
388
- }
389
- } else {
390
- // Error
391
- disconnect(&client);
392
- }
393
- return ServerKit::Channel::Result(buffer.size(), false);
394
- }
395
-
396
- virtual void deinitializeRequest(Client *client, Request *req) {
397
- req->body.clear();
398
- if (!req->jsonBody.isNull()) {
399
- req->jsonBody = Json::Value();
400
- }
401
- ParentClass::deinitializeRequest(client, req);
402
- }
403
-
404
- public:
405
- EventFd *exitEvent;
406
- vector<Authorization> authorizations;
407
-
408
- AdminServer(ServerKit::Context *context)
409
- : ParentClass(context),
410
- exitEvent(NULL)
411
- { }
412
-
413
- virtual StaticString getServerName() const {
414
- return P_STATIC_STRING("WatchdogAdminServer");
415
- }
416
-
417
- static PrivilegeLevel parseLevel(const StaticString &level) {
418
- if (level == "readonly") {
419
- return READONLY;
420
- } else if (level == "full") {
421
- return FULL;
422
- } else {
423
- throw RuntimeException("Invalid privilege level " + level);
424
- }
425
- }
426
- };
427
-
428
-
429
- } // namespace WatchdogAgent
430
- } // namespace Passenger
431
-
432
- #endif /* _PASSENGER_WATCHDOG_AGENT_ADMIN_SERVER_H_ */