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,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2014 Phusion
3
+ * Copyright (c) 2010-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -55,12 +55,12 @@ loggingAgentUsage() {
55
55
  printf(" unix:PATH for Unix domain sockets.\n");
56
56
  printf(" " DEFAULT_LOGGING_AGENT_LISTEN_ADDRESS "\n");
57
57
  printf("\n");
58
- printf(" --admin-listen ADDRESS Listen on the given address for admin commands.\n");
58
+ printf(" --api-listen ADDRESS Listen on the given address for API commands.\n");
59
59
  printf(" The address must be in the same format as that\n");
60
- printf(" of --listen. Default: " DEFAULT_LOGGING_AGENT_ADMIN_LISTEN_ADDRESS "\n");
60
+ printf(" of --listen. Default: " DEFAULT_LOGGING_AGENT_API_LISTEN_ADDRESS "\n");
61
61
  printf(" --authorize [LEVEL]:USERNAME:PASSWORDFILE\n");
62
- printf(" Enables authentication on the admin server,\n");
63
- printf(" through the given admin account. LEVEL indicates\n");
62
+ printf(" Enables authentication on the API server,\n");
63
+ printf(" through the given API account. LEVEL indicates\n");
64
64
  printf(" the privilege level (see below). PASSWORDFILE must\n");
65
65
  printf(" point to a file containing the password\n");
66
66
  printf("\n");
@@ -78,7 +78,7 @@ loggingAgentUsage() {
78
78
  printf("\n");
79
79
  printf(" -h, --help Show this help\n");
80
80
  printf("\n");
81
- printf("Admin account privilege levels (ordered from most to least privileges):\n");
81
+ printf("API account privilege levels (ordered from most to least privileges):\n");
82
82
  printf(" readonly Read-only access\n");
83
83
  printf(" full Full access (default)\n");
84
84
  }
@@ -103,20 +103,20 @@ parseLoggingAgentOption(int argc, const char *argv[], int &i, VariantMap &option
103
103
  "for Unix domain sockets.\n");
104
104
  exit(1);
105
105
  }
106
- } else if (p.isValueFlag(argc, i, argv[i], '\0', "--admin-listen")) {
106
+ } else if (p.isValueFlag(argc, i, argv[i], '\0', "--api-listen")) {
107
107
  if (getSocketAddressType(argv[i + 1]) != SAT_UNKNOWN) {
108
- vector<string> addresses = options.getStrSet("logging_agent_admin_addresses",
108
+ vector<string> addresses = options.getStrSet("logging_agent_api_addresses",
109
109
  false);
110
110
  if (addresses.size() == SERVER_KIT_MAX_SERVER_ENDPOINTS) {
111
- fprintf(stderr, "ERROR: you may specify up to %u --admin-listen addresses.\n",
111
+ fprintf(stderr, "ERROR: you may specify up to %u --api-listen addresses.\n",
112
112
  SERVER_KIT_MAX_SERVER_ENDPOINTS);
113
113
  exit(1);
114
114
  }
115
115
  addresses.push_back(argv[i + 1]);
116
- options.setStrSet("logging_agent_admin_addresses", addresses);
116
+ options.setStrSet("logging_agent_api_addresses", addresses);
117
117
  i += 2;
118
118
  } else {
119
- fprintf(stderr, "ERROR: invalid address format for --admin-listen. The address "
119
+ fprintf(stderr, "ERROR: invalid address format for --api-listen. The address "
120
120
  "must be formatted as tcp://IP:PORT for TCP sockets, or unix:PATH "
121
121
  "for Unix domain sockets.\n");
122
122
  exit(1);
@@ -0,0 +1,311 @@
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_API_SERVER_H_
26
+ #define _PASSENGER_WATCHDOG_AGENT_API_SERVER_H_
27
+
28
+ #include <sstream>
29
+ #include <string>
30
+ #include <cstring>
31
+
32
+ #include <agents/ApiServerUtils.h>
33
+ #include <ServerKit/HttpServer.h>
34
+ #include <DataStructures/LString.h>
35
+ #include <Exceptions.h>
36
+ #include <StaticString.h>
37
+ #include <Logging.h>
38
+ #include <Constants.h>
39
+ #include <Utils/StrIntUtils.h>
40
+ #include <Utils/modp_b64.h>
41
+ #include <Utils/json.h>
42
+ #include <Utils/MessageIO.h>
43
+
44
+ namespace Passenger {
45
+ namespace WatchdogAgent {
46
+
47
+ using namespace std;
48
+
49
+
50
+ class Request: public ServerKit::BaseHttpRequest {
51
+ public:
52
+ string body;
53
+ Json::Value jsonBody;
54
+
55
+ DEFINE_SERVER_KIT_BASE_HTTP_REQUEST_FOOTER(Request);
56
+ };
57
+
58
+ class ApiServer: public ServerKit::HttpServer<ApiServer, ServerKit::HttpClient<Request> > {
59
+ private:
60
+ typedef ServerKit::HttpServer<ApiServer, ServerKit::HttpClient<Request> > ParentClass;
61
+ typedef ServerKit::HttpClient<Request> Client;
62
+ typedef ServerKit::HeaderTable HeaderTable;
63
+
64
+ void route(Client *client, Request *req, const StaticString &path) {
65
+ if (path == P_STATIC_STRING("/status.txt")) {
66
+ processStatusTxt(client, req);
67
+ } else if (path == P_STATIC_STRING("/ping.json")) {
68
+ apiServerProcessPing(this, client, req);
69
+ } else if (path == P_STATIC_STRING("/version.json")) {
70
+ apiServerProcessVersion(this, client, req);
71
+ } else if (path == P_STATIC_STRING("/shutdown.json")) {
72
+ apiServerProcessShutdown(this, client, req);
73
+ } else if (path == P_STATIC_STRING("/backtraces.txt")) {
74
+ apiServerProcessBacktraces(this, client, req);
75
+ } else if (path == P_STATIC_STRING("/config.json")) {
76
+ processConfig(client, req);
77
+ } else if (path == P_STATIC_STRING("/config/log_file.fd")) {
78
+ processConfigLogFileFd(client, req);
79
+ } else if (path == P_STATIC_STRING("/reopen_logs.json")) {
80
+ apiServerProcessReopenLogs(this, client, req);
81
+ } else {
82
+ apiServerRespondWith404(this, client, req);
83
+ }
84
+ }
85
+
86
+ void processStatusTxt(Client *client, Request *req) {
87
+ if (authorizeStateInspectionOperation(this, client, req)) {
88
+ HeaderTable headers;
89
+ //stringstream stream;
90
+ headers.insert(req->pool, "Content-Type", "text/plain");
91
+ //loggingServer->dump(stream);
92
+ //writeSimpleResponse(client, 200, &headers, stream.str());
93
+ if (!req->ended()) {
94
+ endRequest(&client, &req);
95
+ }
96
+ } else {
97
+ apiServerRespondWith401(this, client, req);
98
+ }
99
+ }
100
+
101
+ void processConfig(Client *client, Request *req) {
102
+ if (req->method == HTTP_GET) {
103
+ if (!authorizeStateInspectionOperation(this, client, req)) {
104
+ apiServerRespondWith401(this, client, req);
105
+ }
106
+
107
+ HeaderTable headers;
108
+ Json::Value doc;
109
+ string logFile = getLogFile();
110
+ string fileDescriptorLogFile = getFileDescriptorLogFile();
111
+
112
+ headers.insert(req->pool, "Content-Type", "application/json");
113
+ doc["log_level"] = getLogLevel();
114
+ if (!logFile.empty()) {
115
+ doc["log_file"] = logFile;
116
+ }
117
+ if (!fileDescriptorLogFile.empty()) {
118
+ doc["file_descriptor_log_file"] = fileDescriptorLogFile;
119
+ }
120
+
121
+ writeSimpleResponse(client, 200, &headers, doc.toStyledString());
122
+ if (!req->ended()) {
123
+ endRequest(&client, &req);
124
+ }
125
+ } else if (req->method == HTTP_PUT) {
126
+ if (!authorizeAdminOperation(this, client, req)) {
127
+ apiServerRespondWith401(this, client, req);
128
+ } else if (!req->hasBody()) {
129
+ endAsBadRequest(&client, &req, "Body required");
130
+ }
131
+ // Continue in processConfigBody()
132
+ } else {
133
+ apiServerRespondWith405(this, client, req);
134
+ }
135
+ }
136
+
137
+ void processConfigBody(Client *client, Request *req) {
138
+ HeaderTable headers;
139
+ Json::Value &json = req->jsonBody;
140
+
141
+ headers.insert(req->pool, "Content-Type", "application/json");
142
+
143
+ if (json.isMember("log_level")) {
144
+ setLogLevel(json["log_level"].asInt());
145
+ }
146
+ if (json.isMember("log_file")) {
147
+ string logFile = json["log_file"].asString();
148
+ try {
149
+ logFile = absolutizePath(logFile);
150
+ } catch (const SystemException &e) {
151
+ unsigned int bufsize = 1024;
152
+ char *message = (char *) psg_pnalloc(req->pool, bufsize);
153
+ snprintf(message, bufsize, "{ \"status\": \"error\", "
154
+ "\"message\": \"Cannot absolutize log file filename: %s\" }",
155
+ e.what());
156
+ writeSimpleResponse(client, 500, &headers, message);
157
+ if (!req->ended()) {
158
+ endRequest(&client, &req);
159
+ }
160
+ return;
161
+ }
162
+
163
+ int e;
164
+ if (!setLogFile(logFile, &e)) {
165
+ unsigned int bufsize = 1024;
166
+ char *message = (char *) psg_pnalloc(req->pool, bufsize);
167
+ snprintf(message, bufsize, "{ \"status\": \"error\", "
168
+ "\"message\": \"Cannot open log file: %s (errno=%d)\" }",
169
+ strerror(e), e);
170
+ writeSimpleResponse(client, 500, &headers, message);
171
+ if (!req->ended()) {
172
+ endRequest(&client, &req);
173
+ }
174
+ return;
175
+ }
176
+ P_NOTICE("Log file opened.");
177
+ }
178
+
179
+ writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
180
+ if (!req->ended()) {
181
+ endRequest(&client, &req);
182
+ }
183
+ }
184
+
185
+ bool authorizeFdPassingOperation(Client *client, Request *req) {
186
+ const LString *password = req->headers.lookup("fd-passing-password");
187
+ if (password == NULL) {
188
+ return false;
189
+ }
190
+
191
+ password = psg_lstr_make_contiguous(password, req->pool);
192
+ return constantTimeCompare(StaticString(password->start->data, password->size),
193
+ fdPassingPassword);
194
+ }
195
+
196
+ void processConfigLogFileFd(Client *client, Request *req) {
197
+ if (req->method != HTTP_GET) {
198
+ apiServerRespondWith405(this, client, req);
199
+ } else if (authorizeFdPassingOperation(client, req)) {
200
+ HeaderTable headers;
201
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
202
+ headers.insert(req->pool, "Content-Type", "text/plain");
203
+ headers.insert(req->pool, "Filename", getLogFile());
204
+ req->wantKeepAlive = false;
205
+ writeSimpleResponse(client, 200, &headers, "");
206
+ if (req->ended()) {
207
+ return;
208
+ }
209
+
210
+ unsigned long long timeout = 1000000;
211
+ setBlocking(client->getFd());
212
+ writeFileDescriptorWithNegotiation(client->getFd(), STDERR_FILENO,
213
+ &timeout);
214
+ setNonBlocking(client->getFd());
215
+
216
+ if (!req->ended()) {
217
+ endRequest(&client, &req);
218
+ }
219
+ } else {
220
+ apiServerRespondWith401(this, client, req);
221
+ }
222
+ }
223
+
224
+ protected:
225
+ virtual void onRequestBegin(Client *client, Request *req) {
226
+ const StaticString path(req->path.start->data, req->path.size);
227
+
228
+ P_INFO("API request: " << http_method_str(req->method) <<
229
+ " " << StaticString(req->path.start->data, req->path.size));
230
+
231
+ try {
232
+ route(client, req, path);
233
+ } catch (const oxt::tracable_exception &e) {
234
+ SKC_ERROR(client, "Exception: " << e.what() << "\n" << e.backtrace());
235
+ if (!req->ended()) {
236
+ req->wantKeepAlive = false;
237
+ endRequest(&client, &req);
238
+ }
239
+ }
240
+ }
241
+
242
+ virtual ServerKit::Channel::Result onRequestBody(Client *client, Request *req,
243
+ const MemoryKit::mbuf &buffer, int errcode)
244
+ {
245
+ if (buffer.size() > 0) {
246
+ // Data
247
+ req->body.append(buffer.start, buffer.size());
248
+ } else if (errcode == 0) {
249
+ // EOF
250
+ Json::Reader reader;
251
+ if (reader.parse(req->body, req->jsonBody)) {
252
+ try {
253
+ processConfigBody(client, req);
254
+ } catch (const oxt::tracable_exception &e) {
255
+ SKC_ERROR(client, "Exception: " << e.what() << "\n" << e.backtrace());
256
+ if (!req->ended()) {
257
+ req->wantKeepAlive = false;
258
+ endRequest(&client, &req);
259
+ }
260
+ }
261
+ } else {
262
+ apiServerRespondWith422(this, client, req, reader.getFormattedErrorMessages());
263
+ }
264
+ } else {
265
+ // Error
266
+ disconnect(&client);
267
+ }
268
+ return ServerKit::Channel::Result(buffer.size(), false);
269
+ }
270
+
271
+ virtual void deinitializeRequest(Client *client, Request *req) {
272
+ req->body.clear();
273
+ if (!req->jsonBody.isNull()) {
274
+ req->jsonBody = Json::Value();
275
+ }
276
+ ParentClass::deinitializeRequest(client, req);
277
+ }
278
+
279
+ public:
280
+ ApiAccountDatabase *apiAccountDatabase;
281
+ EventFd *exitEvent;
282
+ string fdPassingPassword;
283
+
284
+ ApiServer(ServerKit::Context *context)
285
+ : ParentClass(context),
286
+ apiAccountDatabase(NULL),
287
+ exitEvent(NULL)
288
+ { }
289
+
290
+ virtual StaticString getServerName() const {
291
+ return P_STATIC_STRING("WatchdogApiServer");
292
+ }
293
+
294
+ virtual unsigned int getClientName(const Client *client, char *buf, size_t size) const {
295
+ return ParentClass::getClientName(client, buf, size);
296
+ }
297
+
298
+ bool authorizeByUid(uid_t uid) const {
299
+ return uid == 0 || uid == geteuid();
300
+ }
301
+
302
+ bool authorizeByApiKey(const ApplicationPool2::ApiKey &apiKey) const {
303
+ return apiKey.isSuper();
304
+ }
305
+ };
306
+
307
+
308
+ } // namespace WatchdogAgent
309
+ } // namespace Passenger
310
+
311
+ #endif /* _PASSENGER_WATCHDOG_AGENT_API_SERVER_H_ */
@@ -22,6 +22,9 @@
22
22
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
23
  * THE SOFTWARE.
24
24
  */
25
+ // Include ev++.h early to avoid macro clash on EV_ERROR.
26
+ #include <ev++.h>
27
+
25
28
  #include <oxt/thread.hpp>
26
29
  #include <oxt/system_calls.hpp>
27
30
  #include <boost/function.hpp>
@@ -55,9 +58,10 @@
55
58
  #include <cerrno>
56
59
 
57
60
  #include <agents/Base.h>
61
+ #include <agents/ApiServerUtils.h>
58
62
  #include <agents/HelperAgent/OptionParser.h>
59
63
  #include <agents/LoggingAgent/OptionParser.h>
60
- #include <agents/Watchdog/AdminServer.h>
64
+ #include <agents/Watchdog/ApiServer.h>
61
65
  #include <Constants.h>
62
66
  #include <InstanceDirectory.h>
63
67
  #include <FileDescriptor.h>
@@ -113,11 +117,11 @@ namespace WatchdogAgent {
113
117
  bool pidsCleanedUp;
114
118
  bool pidFileCleanedUp;
115
119
 
116
- vector<AdminServer::Authorization> adminAuthorizations;
117
- int adminServerFds[SERVER_KIT_MAX_SERVER_ENDPOINTS];
120
+ ApiAccountDatabase apiAccountDatabase;
121
+ int apiServerFds[SERVER_KIT_MAX_SERVER_ENDPOINTS];
118
122
  BackgroundEventLoop *bgloop;
119
123
  ServerKit::Context *serverKitContext;
120
- AdminServer *adminServer;
124
+ ApiServer *apiServer;
121
125
 
122
126
  WorkingObjects()
123
127
  : errorEvent(__FILE__, __LINE__, "WorkingObjects: errorEvent"),
@@ -127,10 +131,10 @@ namespace WatchdogAgent {
127
131
  pidFileCleanedUp(false),
128
132
  bgloop(NULL),
129
133
  serverKitContext(NULL),
130
- adminServer(NULL)
134
+ apiServer(NULL)
131
135
  {
132
136
  for (unsigned int i = 0; i < SERVER_KIT_MAX_SERVER_ENDPOINTS; i++) {
133
- adminServerFds[i] = -1;
137
+ apiServerFds[i] = -1;
134
138
  }
135
139
  }
136
140
  };
@@ -293,11 +297,11 @@ waitForStarterProcessOrWatchers(const WorkingObjectsPtr &wo, vector<AgentWatcher
293
297
  sigaction(SIGINT, &action, NULL);
294
298
  sigaction(SIGTERM, &action, NULL);
295
299
 
296
- P_DEBUG("Stopping admin server");
300
+ P_DEBUG("Stopping API server");
297
301
  wo->bgloop->stop();
298
302
  for (unsigned int i = 0; i < SERVER_KIT_MAX_SERVER_ENDPOINTS; i++) {
299
- if (wo->adminServerFds[i] != -1) {
300
- syscalls::close(wo->adminServerFds[i]);
303
+ if (wo->apiServerFds[i] != -1) {
304
+ syscalls::close(wo->apiServerFds[i]);
301
305
  }
302
306
  }
303
307
 
@@ -558,16 +562,15 @@ usage() {
558
562
  printf(" logging server\n");
559
563
  printf("\n");
560
564
  printf("Other options (optional):\n");
561
- printf(" --admin-listen ADDRESS\n");
562
- printf(" Listen on the given address for admin commands.\n");
565
+ printf(" --api-listen ADDRESS Listen on the given address for API commands.\n");
563
566
  printf(" The address must be formatted as tcp://IP:PORT for\n");
564
567
  printf(" TCP sockets, or unix:PATH for Unix domain sockets.\n");
565
568
  printf(" You can specify this option multiple times (up to\n");
566
569
  printf(" %u times) to listen on multiple addresses.\n",
567
570
  SERVER_KIT_MAX_SERVER_ENDPOINTS - 1);
568
571
  printf(" --authorize [LEVEL]:USERNAME:PASSWORDFILE\n");
569
- printf(" Enables authentication on the admin server, through\n");
570
- printf(" the given admin account. LEVEL indicates the\n");
572
+ printf(" Enables authentication on the API server, through\n");
573
+ printf(" the given API account. LEVEL indicates the\n");
571
574
  printf(" privilege level (see below). PASSWORDFILE must\n");
572
575
  printf(" point to a file containing the password\n");
573
576
  printf("\n");
@@ -599,7 +602,7 @@ usage() {
599
602
  printf("\n");
600
603
  printf("[A] = Automatically passed to supervised agents\n");
601
604
  printf("\n");
602
- printf("Admin account privilege levels (ordered from most to least privileges):\n");
605
+ printf("API account privilege levels (ordered from most to least privileges):\n");
603
606
  printf(" readonly Read-only access\n");
604
607
  printf(" full Full access (default)\n");
605
608
  }
@@ -653,20 +656,20 @@ parseOptions(int argc, const char *argv[], VariantMap &options) {
653
656
  exit(1);
654
657
  }
655
658
  }
656
- } else if (p.isValueFlag(argc, i, argv[i], '\0', "--admin-listen")) {
659
+ } else if (p.isValueFlag(argc, i, argv[i], '\0', "--api-listen")) {
657
660
  if (getSocketAddressType(argv[i + 1]) != SAT_UNKNOWN) {
658
- vector<string> addresses = options.getStrSet("watchdog_admin_addresses",
661
+ vector<string> addresses = options.getStrSet("watchdog_api_addresses",
659
662
  false);
660
663
  if (addresses.size() == SERVER_KIT_MAX_SERVER_ENDPOINTS - 1) {
661
- fprintf(stderr, "ERROR: you may specify up to %u --admin-listen addresses.\n",
664
+ fprintf(stderr, "ERROR: you may specify up to %u --api-listen addresses.\n",
662
665
  SERVER_KIT_MAX_SERVER_ENDPOINTS - 1);
663
666
  exit(1);
664
667
  }
665
668
  addresses.push_back(argv[i + 1]);
666
- options.setStrSet("watchdog_admin_addresses", addresses);
669
+ options.setStrSet("watchdog_api_addresses", addresses);
667
670
  i += 2;
668
671
  } else {
669
- fprintf(stderr, "ERROR: invalid address format for --admin-listen. The address "
672
+ fprintf(stderr, "ERROR: invalid address format for --api-listen. The address "
670
673
  "must be formatted as tcp://IP:PORT for TCP sockets, or unix:PATH "
671
674
  "for Unix domain sockets.\n");
672
675
  exit(1);
@@ -1016,6 +1019,7 @@ initializeWorkingObjects(const WorkingObjectsPtr &wo, InstanceDirToucherPtr &ins
1016
1019
  fullAdminPassword, S_IRUSR | S_IWUSR);
1017
1020
  }
1018
1021
  options.setDefault("server_pid_file", wo->instanceDir->getPath() + "/server.pid");
1022
+ options.set("watchdog_fd_passing_password", wo->randomGenerator.generateAsciiString(24));
1019
1023
 
1020
1024
  UPDATE_TRACE_POINT();
1021
1025
  strset = options.getStrSet("server_addresses", false);
@@ -1025,20 +1029,20 @@ initializeWorkingObjects(const WorkingObjectsPtr &wo, InstanceDirToucherPtr &ins
1025
1029
  options.setDefault("server_password",
1026
1030
  wo->randomGenerator.generateAsciiString(24));
1027
1031
 
1028
- strset = options.getStrSet("server_admin_addresses", false);
1032
+ strset = options.getStrSet("server_api_addresses", false);
1029
1033
  strset.insert(strset.begin(),
1030
- "unix:" + wo->instanceDir->getPath() + "/agents.s/server_admin");
1031
- options.setStrSet("server_admin_addresses", strset);
1034
+ "unix:" + wo->instanceDir->getPath() + "/agents.s/server_api");
1035
+ options.setStrSet("server_api_addresses", strset);
1032
1036
 
1033
1037
  UPDATE_TRACE_POINT();
1034
1038
  options.setDefault("logging_agent_address",
1035
1039
  "unix:" + wo->instanceDir->getPath() + "/agents.s/logging");
1036
1040
  options.setDefault("logging_agent_password",
1037
1041
  wo->randomGenerator.generateAsciiString(24));
1038
- strset = options.getStrSet("logging_agent_admin_addresses", false);
1042
+ strset = options.getStrSet("logging_agent_api_addresses", false);
1039
1043
  strset.insert(strset.begin(),
1040
- "unix:" + wo->instanceDir->getPath() + "/agents.s/logging_admin");
1041
- options.setStrSet("logging_agent_admin_addresses", strset);
1044
+ "unix:" + wo->instanceDir->getPath() + "/agents.s/logging_api");
1045
+ options.setStrSet("logging_agent_api_addresses", strset);
1042
1046
 
1043
1047
  UPDATE_TRACE_POINT();
1044
1048
  strset = options.getStrSet("logging_agent_authorizations", false);
@@ -1077,34 +1081,11 @@ makeFileWorldReadableAndWritable(const string &path) {
1077
1081
  }
1078
1082
 
1079
1083
  static void
1080
- parseAndAddAdminAuthorization(const WorkingObjectsPtr &wo, const string &description) {
1081
- TRACE_POINT();
1082
- AdminServer::Authorization auth;
1083
- vector<string> args;
1084
-
1085
- split(description, ':', args);
1086
-
1087
- if (args.size() == 2) {
1088
- auth.level = AdminServer::FULL;
1089
- auth.username = args[0];
1090
- auth.password = strip(readAll(args[1]));
1091
- } else if (args.size() == 3) {
1092
- auth.level = AdminServer::parseLevel(args[0]);
1093
- auth.username = args[1];
1094
- auth.password = strip(readAll(args[2]));
1095
- } else {
1096
- P_BUG("Too many elements in authorization description");
1097
- }
1098
-
1099
- wo->adminAuthorizations.push_back(auth);
1100
- }
1101
-
1102
- static void
1103
- initializeAdminServer(const WorkingObjectsPtr &wo) {
1084
+ initializeApiServer(const WorkingObjectsPtr &wo) {
1104
1085
  TRACE_POINT();
1105
1086
  VariantMap &options = *agentsOptions;
1106
1087
  vector<string> authorizations = options.getStrSet("watchdog_authorizations", false);
1107
- vector<string> adminAddresses = options.getStrSet("watchdog_admin_addresses", false);
1088
+ vector<string> apiAddresses = options.getStrSet("watchdog_api_addresses", false);
1108
1089
  string description;
1109
1090
 
1110
1091
  UPDATE_TRACE_POINT();
@@ -1117,21 +1098,25 @@ initializeAdminServer(const WorkingObjectsPtr &wo) {
1117
1098
  options.setStrSet("watchdog_authorizations", authorizations);
1118
1099
 
1119
1100
  foreach (description, authorizations) {
1120
- parseAndAddAdminAuthorization(wo, description);
1101
+ try {
1102
+ wo->apiAccountDatabase.add(description);
1103
+ } catch (const ArgumentException &e) {
1104
+ throw std::runtime_error(e.what());
1105
+ }
1121
1106
  }
1122
1107
 
1123
1108
  UPDATE_TRACE_POINT();
1124
- adminAddresses.insert(adminAddresses.begin(),
1125
- "unix:" + wo->instanceDir->getPath() + "/agents.s/watchdog");
1126
- options.setStrSet("watchdog_admin_addresses", adminAddresses);
1109
+ apiAddresses.insert(apiAddresses.begin(),
1110
+ "unix:" + wo->instanceDir->getPath() + "/agents.s/watchdog_api");
1111
+ options.setStrSet("watchdog_api_addresses", apiAddresses);
1127
1112
 
1128
1113
  UPDATE_TRACE_POINT();
1129
- for (unsigned int i = 0; i < adminAddresses.size(); i++) {
1130
- P_DEBUG("Admin server will listen on " << adminAddresses[i]);
1131
- wo->adminServerFds[i] = createServer(adminAddresses[i], 0, true,
1114
+ for (unsigned int i = 0; i < apiAddresses.size(); i++) {
1115
+ P_DEBUG("API server will listen on " << apiAddresses[i]);
1116
+ wo->apiServerFds[i] = createServer(apiAddresses[i], 0, true,
1132
1117
  __FILE__, __LINE__);
1133
- if (getSocketAddressType(adminAddresses[i]) == SAT_UNIX) {
1134
- makeFileWorldReadableAndWritable(parseUnixSocketAddress(adminAddresses[i]));
1118
+ if (getSocketAddressType(apiAddresses[i]) == SAT_UNIX) {
1119
+ makeFileWorldReadableAndWritable(parseUnixSocketAddress(apiAddresses[i]));
1135
1120
  }
1136
1121
  }
1137
1122
 
@@ -1143,11 +1128,51 @@ initializeAdminServer(const WorkingObjectsPtr &wo) {
1143
1128
  absolutizePath(options.get("data_buffer_dir"));
1144
1129
 
1145
1130
  UPDATE_TRACE_POINT();
1146
- wo->adminServer = new AdminServer(wo->serverKitContext);
1147
- wo->adminServer->exitEvent = &wo->exitEvent;
1148
- wo->adminServer->authorizations = wo->adminAuthorizations;
1149
- for (unsigned int i = 0; i < adminAddresses.size(); i++) {
1150
- wo->adminServer->listen(wo->adminServerFds[i]);
1131
+ wo->apiServer = new ApiServer(wo->serverKitContext);
1132
+ wo->apiServer->apiAccountDatabase = &wo->apiAccountDatabase;
1133
+ wo->apiServer->exitEvent = &wo->exitEvent;
1134
+ wo->apiServer->fdPassingPassword = options.get("watchdog_fd_passing_password");
1135
+ for (unsigned int i = 0; i < apiAddresses.size(); i++) {
1136
+ wo->apiServer->listen(wo->apiServerFds[i]);
1137
+ }
1138
+ }
1139
+
1140
+ static void
1141
+ createCompatSymlinks(const WorkingObjectsPtr &wo) {
1142
+ /* In 5.0.10, 'watchdog' has been renamed to 'watchdog_api',
1143
+ * 'server_admin' has been renamed to 'server_api',
1144
+ * and 'logging_admin' has been renamed to 'logging_api'.
1145
+ * To maintain backward compatibility with older versions of
1146
+ * passenger-status etc, we create compatibility symlinks.
1147
+ */
1148
+ int ret, e;
1149
+ string prefix = wo->instanceDir->getPath() + "/agents.s/";
1150
+
1151
+ do {
1152
+ ret = symlink("watchdog_api", (prefix + "watchdog").c_str());
1153
+ } while (ret == -1 && errno == EAGAIN);
1154
+ if (ret == -1) {
1155
+ e = errno;
1156
+ throw FileSystemException("Cannot create symlink: " + prefix + "watchdog",
1157
+ e, prefix + "watchdog");
1158
+ }
1159
+
1160
+ do {
1161
+ ret = symlink("server_api", (prefix + "server_admin").c_str());
1162
+ } while (ret == -1 && errno == EAGAIN);
1163
+ if (ret == -1) {
1164
+ e = errno;
1165
+ throw FileSystemException("Cannot create symlink: " + prefix + "server_admin",
1166
+ e, prefix + "server_admin");
1167
+ }
1168
+
1169
+ do {
1170
+ ret = symlink("logging_api", (prefix + "logging_admin").c_str());
1171
+ } while (ret == -1 && errno == EAGAIN);
1172
+ if (ret == -1) {
1173
+ e = errno;
1174
+ throw FileSystemException("Cannot create symlink: " + prefix + "logging_admin",
1175
+ e, prefix + "logging_admin");
1151
1176
  }
1152
1177
  }
1153
1178
 
@@ -1280,7 +1305,8 @@ watchdogMain(int argc, char *argv[]) {
1280
1305
  lowerPrivilege();
1281
1306
  initializeWorkingObjects(wo, instanceDirToucher, uidBeforeLoweringPrivilege);
1282
1307
  initializeAgentWatchers(wo, watchers);
1283
- initializeAdminServer(wo);
1308
+ initializeApiServer(wo);
1309
+ createCompatSymlinks(wo);
1284
1310
  UPDATE_TRACE_POINT();
1285
1311
  runHookScriptAndThrowOnError("before_watchdog_initialization");
1286
1312
  } catch (const std::exception &e) {