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.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/CHANGELOG +15 -0
- data/CONTRIBUTORS +6 -0
- data/README.md +1 -1
- data/bin/passenger-install-apache2-module +24 -11
- data/bin/passenger-status +29 -14
- data/build/agents.rb +12 -10
- data/build/cxx_tests.rb +30 -30
- data/doc/Design and Architecture.html +1 -10
- data/doc/Design and Architecture.txt +1 -6
- data/doc/Users guide Apache.html +1 -19
- data/doc/Users guide Apache.txt +1 -1
- data/doc/Users guide Nginx.html +2 -20
- data/doc/Users guide Nginx.txt +2 -2
- data/doc/users_guide_snippets/tips.txt +0 -9
- data/ext/common/ApplicationPool2/ApiKey.h +158 -0
- data/ext/common/ApplicationPool2/BasicGroupInfo.h +81 -0
- data/ext/common/ApplicationPool2/BasicProcessInfo.h +106 -0
- data/ext/common/ApplicationPool2/Common.h +5 -44
- data/ext/common/ApplicationPool2/Context.h +94 -0
- data/ext/common/ApplicationPool2/Group.h +130 -1205
- data/ext/common/ApplicationPool2/Group/InitializationAndShutdown.cpp +190 -0
- data/ext/common/ApplicationPool2/Group/InternalUtils.cpp +329 -0
- data/ext/common/ApplicationPool2/Group/LifetimeAndBasics.cpp +103 -0
- data/ext/common/ApplicationPool2/{Pool/Debug.h → Group/Miscellaneous.cpp} +40 -38
- data/ext/common/ApplicationPool2/Group/OutOfBandWork.cpp +323 -0
- data/ext/common/ApplicationPool2/Group/ProcessListManagement.cpp +606 -0
- data/ext/common/ApplicationPool2/Group/SessionManagement.cpp +337 -0
- data/ext/common/ApplicationPool2/Group/SpawningAndRestarting.cpp +478 -0
- data/ext/common/ApplicationPool2/Group/StateInspection.cpp +197 -0
- data/ext/common/ApplicationPool2/Group/Verification.cpp +159 -0
- data/ext/common/ApplicationPool2/Implementation.cpp +19 -1401
- data/ext/common/ApplicationPool2/Options.h +5 -5
- data/ext/common/ApplicationPool2/Pool.h +260 -815
- data/ext/common/ApplicationPool2/Pool/{AnalyticsCollection.h → AnalyticsCollection.cpp} +55 -56
- data/ext/common/ApplicationPool2/Pool/{GarbageCollection.h → GarbageCollection.cpp} +49 -49
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.cpp +241 -0
- data/ext/common/ApplicationPool2/Pool/GroupUtils.cpp +276 -0
- data/ext/common/ApplicationPool2/Pool/InitializationAndShutdown.cpp +145 -0
- data/ext/common/ApplicationPool2/Pool/Miscellaneous.cpp +244 -0
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.cpp +330 -0
- data/ext/common/ApplicationPool2/Pool/StateInspection.cpp +299 -0
- data/ext/common/ApplicationPool2/Process.h +399 -205
- data/ext/common/ApplicationPool2/Session.h +70 -28
- data/ext/common/ApplicationPool2/Socket.h +1 -0
- data/ext/common/Constants.h +11 -3
- data/ext/common/Exceptions.h +1 -1
- data/ext/common/Logging.cpp +9 -4
- data/ext/common/Logging.h +6 -0
- data/ext/common/ServerKit/HttpServer.h +225 -215
- data/ext/common/ServerKit/Server.h +57 -57
- data/ext/common/SpawningKit/BackgroundIOCapturer.h +160 -0
- data/ext/common/SpawningKit/Config.h +107 -0
- data/ext/common/{ApplicationPool2 → SpawningKit}/DirectSpawner.h +17 -16
- data/ext/common/{ApplicationPool2 → SpawningKit}/DummySpawner.h +33 -33
- data/ext/common/{ApplicationPool2/SpawnerFactory.h → SpawningKit/Factory.h} +17 -17
- data/ext/common/{ApplicationPool2/ComponentInfo.h → SpawningKit/Options.h} +8 -21
- data/ext/common/SpawningKit/PipeWatcher.h +148 -0
- data/ext/common/{ApplicationPool2/PipeWatcher.h → SpawningKit/Result.h} +15 -33
- data/ext/common/{ApplicationPool2 → SpawningKit}/SmartSpawner.h +52 -57
- data/ext/common/{ApplicationPool2 → SpawningKit}/Spawner.h +83 -371
- data/ext/common/SpawningKit/UserSwitchingRules.h +265 -0
- data/ext/common/Utils/BufferedIO.h +24 -0
- data/ext/common/{ApplicationPool2/SpawnObject.h → Utils/ClassUtils.h} +24 -51
- data/ext/common/Utils/IOUtils.cpp +70 -0
- data/ext/common/Utils/IOUtils.h +19 -0
- data/ext/common/Utils/JsonUtils.h +113 -0
- data/ext/common/Utils/StrIntUtils.h +29 -0
- data/ext/common/Utils/json.h +1 -1
- data/ext/common/agents/ApiServerUtils.h +941 -0
- data/ext/common/agents/HelperAgent/{AdminServer.h → ApiServer.h} +163 -365
- data/ext/common/agents/HelperAgent/Main.cpp +86 -88
- data/ext/common/agents/HelperAgent/OptionParser.h +9 -10
- data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -0
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +2 -0
- data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -1
- data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +2 -2
- data/ext/common/agents/LoggingAgent/ApiServer.h +279 -0
- data/ext/common/agents/LoggingAgent/Main.cpp +41 -51
- data/ext/common/agents/LoggingAgent/OptionParser.h +11 -11
- data/ext/common/agents/Watchdog/ApiServer.h +311 -0
- data/ext/common/agents/Watchdog/Main.cpp +91 -65
- data/helper-scripts/prespawn +2 -0
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/admin_tools/instance.rb +1 -1
- data/lib/phusion_passenger/common_library.rb +27 -14
- data/lib/phusion_passenger/config/{admin_command_command.rb → api_call_command.rb} +19 -16
- data/lib/phusion_passenger/config/detach_process_command.rb +6 -3
- data/lib/phusion_passenger/config/main.rb +3 -5
- data/lib/phusion_passenger/config/reopen_logs_command.rb +29 -7
- data/lib/phusion_passenger/config/restart_app_command.rb +13 -4
- data/lib/phusion_passenger/config/utils.rb +15 -8
- data/lib/phusion_passenger/constants.rb +6 -2
- data/lib/phusion_passenger/platform_info/apache.rb +4 -0
- data/lib/phusion_passenger/platform_info/apache_detector.rb +18 -3
- data/resources/templates/apache2/mpm_unknown.txt.erb +20 -0
- metadata +42 -21
- metadata.gz.asc +7 -7
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.h +0 -127
- data/ext/common/ApplicationPool2/Pool/Inspection.h +0 -219
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.h +0 -85
- data/ext/common/ApplicationPool2/SuperGroup.h +0 -706
- data/ext/common/agents/LoggingAgent/AdminServer.h +0 -435
- 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-
|
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(" --
|
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: "
|
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
|
63
|
-
printf(" through the given
|
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("
|
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', "--
|
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("
|
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 --
|
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("
|
116
|
+
options.setStrSet("logging_agent_api_addresses", addresses);
|
117
117
|
i += 2;
|
118
118
|
} else {
|
119
|
-
fprintf(stderr, "ERROR: invalid address format for --
|
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/
|
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
|
-
|
117
|
-
int
|
120
|
+
ApiAccountDatabase apiAccountDatabase;
|
121
|
+
int apiServerFds[SERVER_KIT_MAX_SERVER_ENDPOINTS];
|
118
122
|
BackgroundEventLoop *bgloop;
|
119
123
|
ServerKit::Context *serverKitContext;
|
120
|
-
|
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
|
-
|
134
|
+
apiServer(NULL)
|
131
135
|
{
|
132
136
|
for (unsigned int i = 0; i < SERVER_KIT_MAX_SERVER_ENDPOINTS; i++) {
|
133
|
-
|
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
|
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->
|
300
|
-
syscalls::close(wo->
|
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(" --
|
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
|
570
|
-
printf(" the given
|
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("
|
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', "--
|
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("
|
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 --
|
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("
|
669
|
+
options.setStrSet("watchdog_api_addresses", addresses);
|
667
670
|
i += 2;
|
668
671
|
} else {
|
669
|
-
fprintf(stderr, "ERROR: invalid address format for --
|
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("
|
1032
|
+
strset = options.getStrSet("server_api_addresses", false);
|
1029
1033
|
strset.insert(strset.begin(),
|
1030
|
-
"unix:" + wo->instanceDir->getPath() + "/agents.s/
|
1031
|
-
options.setStrSet("
|
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("
|
1042
|
+
strset = options.getStrSet("logging_agent_api_addresses", false);
|
1039
1043
|
strset.insert(strset.begin(),
|
1040
|
-
"unix:" + wo->instanceDir->getPath() + "/agents.s/
|
1041
|
-
options.setStrSet("
|
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
|
-
|
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>
|
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
|
-
|
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
|
-
|
1125
|
-
"unix:" + wo->instanceDir->getPath() + "/agents.s/
|
1126
|
-
options.setStrSet("
|
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 <
|
1130
|
-
P_DEBUG("
|
1131
|
-
wo->
|
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(
|
1134
|
-
makeFileWorldReadableAndWritable(parseUnixSocketAddress(
|
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->
|
1147
|
-
wo->
|
1148
|
-
wo->
|
1149
|
-
|
1150
|
-
|
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
|
-
|
1308
|
+
initializeApiServer(wo);
|
1309
|
+
createCompatSymlinks(wo);
|
1284
1310
|
UPDATE_TRACE_POINT();
|
1285
1311
|
runHookScriptAndThrowOnError("before_watchdog_initialization");
|
1286
1312
|
} catch (const std::exception &e) {
|