passenger 4.0.2 → 4.0.3
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.
- data.tar.gz.asc +7 -7
- data/NEWS +27 -0
- data/bin/passenger-config +6 -3
- data/bin/passenger-install-apache2-module +2 -2
- data/bin/passenger-install-nginx-module +16 -2
- data/build/agents.rb +4 -0
- data/build/apache2.rb +1 -1
- data/build/cplusplus_support.rb +1 -1
- data/build/cxx_tests.rb +3 -0
- data/build/packaging.rb +51 -8
- data/build/ruby_extension.rb +1 -1
- data/doc/Packaging.txt.md +20 -7
- data/doc/Users guide Apache.html +1 -1
- data/doc/Users guide Apache.txt +1 -1
- data/doc/Users guide Nginx.html +5 -4
- data/doc/Users guide Nginx.txt +1 -1
- data/doc/users_guide_snippets/installation.txt +5 -3
- data/ext/apache2/Configuration.cpp +12 -0
- data/ext/apache2/Configuration.hpp +7 -4
- data/ext/apache2/Hooks.cpp +29 -19
- data/ext/common/AgentsStarter.cpp +85 -57
- data/ext/common/AgentsStarter.h +570 -42
- data/ext/common/ApplicationPool2/DirectSpawner.h +5 -2
- data/ext/common/ApplicationPool2/Implementation.cpp +7 -1
- data/ext/common/ApplicationPool2/Pool.h +6 -3
- data/ext/common/ApplicationPool2/Process.h +12 -3
- data/ext/common/ApplicationPool2/SmartSpawner.h +2 -1
- data/ext/common/Constants.h +4 -1
- data/ext/common/EventedBufferedInput.h +139 -16
- data/ext/common/MultiLibeio.cpp +4 -2
- data/ext/common/SafeLibev.h +15 -62
- data/ext/common/ServerInstanceDir.h +10 -26
- data/ext/common/Utils.cpp +1 -3
- data/ext/common/Utils.h +1 -1
- data/ext/common/Utils/StrIntUtils.cpp +9 -0
- data/ext/common/Utils/StrIntUtils.h +5 -0
- data/ext/common/Utils/VariantMap.h +63 -14
- data/ext/common/agents/Base.cpp +50 -15
- data/ext/common/agents/HelperAgent/AgentOptions.h +20 -12
- data/ext/common/agents/HelperAgent/FileBackedPipe.h +1 -1
- data/ext/common/agents/HelperAgent/Main.cpp +5 -4
- data/ext/common/agents/HelperAgent/RequestHandler.h +1 -1
- data/ext/common/agents/LoggingAgent/Main.cpp +0 -1
- data/ext/common/agents/LoggingAgent/RemoteSender.h +2 -2
- data/ext/common/agents/SpawnPreparer.cpp +23 -5
- data/ext/common/agents/Watchdog/AgentWatcher.cpp +508 -0
- data/ext/common/agents/Watchdog/HelperAgentWatcher.cpp +93 -0
- data/ext/common/agents/Watchdog/LoggingAgentWatcher.cpp +68 -0
- data/ext/common/agents/Watchdog/Main.cpp +180 -802
- data/ext/common/agents/Watchdog/ServerInstanceDirToucher.cpp +111 -0
- data/ext/nginx/Configuration.c +107 -92
- data/ext/nginx/Configuration.h +1 -0
- data/ext/nginx/ContentHandler.c +6 -6
- data/ext/nginx/ContentHandler.h +1 -1
- data/ext/nginx/config +8 -2
- data/ext/nginx/ngx_http_passenger_module.c +54 -60
- data/ext/nginx/ngx_http_passenger_module.h +6 -6
- data/lib/phusion_passenger.rb +17 -10
- data/lib/phusion_passenger/admin_tools/server_instance.rb +2 -2
- data/lib/phusion_passenger/common_library.rb +0 -1
- data/lib/phusion_passenger/platform_info.rb +10 -1
- data/lib/phusion_passenger/platform_info/depcheck.rb +4 -4
- data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +2 -2
- data/lib/phusion_passenger/platform_info/ruby.rb +7 -0
- data/lib/phusion_passenger/request_handler.rb +119 -42
- data/lib/phusion_passenger/request_handler/thread_handler.rb +25 -22
- data/lib/phusion_passenger/standalone/command.rb +2 -0
- data/lib/phusion_passenger/standalone/runtime_installer.rb +4 -3
- data/lib/phusion_passenger/standalone/start_command.rb +49 -37
- data/resources/templates/nginx/pcre_checksum_could_not_be_verified.txt.erb +11 -0
- data/test/cxx/CxxTestMain.cpp +2 -0
- data/test/cxx/EventedBufferedInputTest.cpp +758 -0
- data/test/cxx/ServerInstanceDirTest.cpp +16 -31
- data/test/cxx/TestSupport.cpp +2 -1
- data/test/cxx/VariantMapTest.cpp +23 -11
- metadata +8 -4
- metadata.gz.asc +7 -7
- data/ext/common/AgentsStarter.hpp +0 -655
- data/lib/phusion_passenger/utils/robust_interruption.rb +0 -173
@@ -0,0 +1,93 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2010-2013 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
|
+
|
26
|
+
class HelperAgentWatcher: public AgentWatcher {
|
27
|
+
protected:
|
28
|
+
string helperAgentFilename;
|
29
|
+
VariantMap params, report;
|
30
|
+
string requestSocketFilename;
|
31
|
+
string messageSocketFilename;
|
32
|
+
|
33
|
+
virtual const char *name() const {
|
34
|
+
return "Phusion Passenger helper agent";
|
35
|
+
}
|
36
|
+
|
37
|
+
virtual string getExeFilename() const {
|
38
|
+
return helperAgentFilename;
|
39
|
+
}
|
40
|
+
|
41
|
+
virtual void execProgram() const {
|
42
|
+
if (hasEnvOption("PASSENGER_RUN_HELPER_AGENT_IN_VALGRIND", false)) {
|
43
|
+
execlp("valgrind", "valgrind", "--dsymutil=yes",
|
44
|
+
helperAgentFilename.c_str(), (char *) 0);
|
45
|
+
} else {
|
46
|
+
execl(helperAgentFilename.c_str(), "PassengerHelperAgent", (char *) 0);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
virtual void sendStartupArguments(pid_t pid, FileDescriptor &fd) {
|
51
|
+
VariantMap options = agentsOptions;
|
52
|
+
params.addTo(options);
|
53
|
+
options.writeToFd(fd);
|
54
|
+
}
|
55
|
+
|
56
|
+
virtual bool processStartupInfo(pid_t pid, FileDescriptor &fd, const vector<string> &args) {
|
57
|
+
if (args[0] == "initialized") {
|
58
|
+
requestSocketFilename = args[1];
|
59
|
+
messageSocketFilename = args[2];
|
60
|
+
return true;
|
61
|
+
} else {
|
62
|
+
return false;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
public:
|
67
|
+
HelperAgentWatcher(const ResourceLocator &resourceLocator) {
|
68
|
+
helperAgentFilename = resourceLocator.getAgentsDir() + "/PassengerHelperAgent";
|
69
|
+
|
70
|
+
report
|
71
|
+
.set("request_socket_filename",
|
72
|
+
agentsOptions.get("request_socket_filename", false,
|
73
|
+
generation->getPath() + "/request"))
|
74
|
+
.set("request_socket_password",
|
75
|
+
agentsOptions.get("request_socket_password", false,
|
76
|
+
randomGenerator->generateAsciiString(REQUEST_SOCKET_PASSWORD_SIZE)))
|
77
|
+
.set("helper_agent_admin_socket_address",
|
78
|
+
agentsOptions.get("helper_agent_admin_socket_address", false,
|
79
|
+
"unix:" + generation->getPath() + "/helper_admin"))
|
80
|
+
.set("helper_agent_exit_password",
|
81
|
+
agentsOptions.get("helper_agent_exit_password", false,
|
82
|
+
randomGenerator->generateAsciiString(MESSAGE_SERVER_MAX_PASSWORD_SIZE)));
|
83
|
+
|
84
|
+
params = report;
|
85
|
+
params
|
86
|
+
.set("logging_agent_address", loggingAgentAddress)
|
87
|
+
.set("logging_agent_password", loggingAgentPassword);
|
88
|
+
}
|
89
|
+
|
90
|
+
virtual void reportAgentsInformation(VariantMap &report) {
|
91
|
+
this->report.addTo(report);
|
92
|
+
}
|
93
|
+
};
|
@@ -0,0 +1,68 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2010-2013 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
|
+
|
26
|
+
class LoggingAgentWatcher: public AgentWatcher {
|
27
|
+
protected:
|
28
|
+
string agentFilename;
|
29
|
+
string socketAddress;
|
30
|
+
|
31
|
+
virtual const char *name() const {
|
32
|
+
return "Phusion Passenger logging agent";
|
33
|
+
}
|
34
|
+
|
35
|
+
virtual string getExeFilename() const {
|
36
|
+
return agentFilename;
|
37
|
+
}
|
38
|
+
|
39
|
+
virtual void execProgram() const {
|
40
|
+
execl(agentFilename.c_str(), "PassengerLoggingAgent", (char *) 0);
|
41
|
+
}
|
42
|
+
|
43
|
+
virtual void sendStartupArguments(pid_t pid, FileDescriptor &fd) {
|
44
|
+
VariantMap options = agentsOptions;
|
45
|
+
options.set("logging_agent_address", loggingAgentAddress);
|
46
|
+
options.set("logging_agent_password", loggingAgentPassword);
|
47
|
+
options.writeToFd(fd);
|
48
|
+
}
|
49
|
+
|
50
|
+
virtual bool processStartupInfo(pid_t pid, FileDescriptor &fd, const vector<string> &args) {
|
51
|
+
if (args[0] == "initialized") {
|
52
|
+
return true;
|
53
|
+
} else {
|
54
|
+
return false;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
public:
|
59
|
+
LoggingAgentWatcher(const ResourceLocator &resourceLocator) {
|
60
|
+
agentFilename = resourceLocator.getAgentsDir() + "/PassengerLoggingAgent";
|
61
|
+
}
|
62
|
+
|
63
|
+
virtual void reportAgentsInformation(VariantMap &report) {
|
64
|
+
report
|
65
|
+
.set("logging_socket_address", loggingAgentAddress)
|
66
|
+
.set("logging_socket_password", loggingAgentPassword);
|
67
|
+
}
|
68
|
+
};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
-
* Copyright (c) 2010-
|
3
|
+
* Copyright (c) 2010-2013 Phusion
|
4
4
|
*
|
5
5
|
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
6
|
*
|
@@ -27,6 +27,7 @@
|
|
27
27
|
#include <boost/function.hpp>
|
28
28
|
#include <boost/bind.hpp>
|
29
29
|
#include <boost/make_shared.hpp>
|
30
|
+
#include <boost/foreach.hpp>
|
30
31
|
#include <boost/enable_shared_from_this.hpp>
|
31
32
|
#include <string>
|
32
33
|
|
@@ -63,713 +64,44 @@ using namespace oxt;
|
|
63
64
|
using namespace Passenger;
|
64
65
|
|
65
66
|
|
67
|
+
enum OomFileType {
|
68
|
+
OOM_ADJ,
|
69
|
+
OOM_SCORE_ADJ
|
70
|
+
};
|
71
|
+
|
72
|
+
#define REQUEST_SOCKET_PASSWORD_SIZE 64
|
73
|
+
|
74
|
+
class ServerInstanceDirToucher;
|
75
|
+
static bool hasEnvOption(const char *name, bool defaultValue = false);
|
76
|
+
static void setOomScore(const StaticString &score);
|
77
|
+
|
78
|
+
|
66
79
|
/** The options that were passed to AgentsStarter. */
|
67
|
-
static VariantMap
|
68
|
-
static pid_t webServerPid;
|
80
|
+
static VariantMap agentsOptions;
|
69
81
|
static string tempDir;
|
70
82
|
static bool userSwitching;
|
71
83
|
static string defaultUser;
|
72
84
|
static string defaultGroup;
|
73
85
|
static uid_t webServerWorkerUid;
|
74
86
|
static gid_t webServerWorkerGid;
|
75
|
-
static string passengerRoot;
|
76
87
|
static string serializedPrestartURLs;
|
77
88
|
|
89
|
+
/***** Working objects *****/
|
78
90
|
static string oldOomScore;
|
91
|
+
static RandomGenerator *randomGenerator;
|
92
|
+
static EventFd *errorEvent;
|
93
|
+
static ResourceLocator *resourceLocator;
|
79
94
|
static ServerInstanceDirPtr serverInstanceDir;
|
80
95
|
static ServerInstanceDir::GenerationPtr generation;
|
96
|
+
static ServerInstanceDirToucher *serverInstanceDirToucher;
|
81
97
|
static string loggingAgentAddress;
|
82
98
|
static string loggingAgentPassword;
|
83
|
-
static RandomGenerator *randomGenerator;
|
84
|
-
static EventFd *errorEvent;
|
85
|
-
|
86
|
-
#define REQUEST_SOCKET_PASSWORD_SIZE 64
|
87
|
-
|
88
|
-
static bool hasEnvOption(const char *name, bool defaultValue = false);
|
89
|
-
static void setOomScore(const StaticString &score);
|
90
|
-
|
91
|
-
|
92
|
-
/**
|
93
|
-
* Abstract base class for watching agent processes.
|
94
|
-
*/
|
95
|
-
class AgentWatcher: public enable_shared_from_this<AgentWatcher> {
|
96
|
-
private:
|
97
|
-
/** The watcher thread. */
|
98
|
-
oxt::thread *thr;
|
99
|
-
|
100
|
-
void threadMain(shared_ptr<AgentWatcher> self) {
|
101
|
-
try {
|
102
|
-
pid_t pid, ret;
|
103
|
-
int status, e;
|
104
|
-
|
105
|
-
while (!this_thread::interruption_requested()) {
|
106
|
-
{
|
107
|
-
lock_guard<boost::mutex> l(lock);
|
108
|
-
pid = this->pid;
|
109
|
-
}
|
110
|
-
|
111
|
-
// Process can be started before the watcher thread is launched.
|
112
|
-
if (pid == 0) {
|
113
|
-
pid = start();
|
114
|
-
}
|
115
|
-
ret = syscalls::waitpid(pid, &status, 0);
|
116
|
-
if (ret == -1 && errno == ECHILD) {
|
117
|
-
/* If the agent is attached to gdb then waitpid()
|
118
|
-
* here can return -1 with errno == ECHILD.
|
119
|
-
* Fallback to kill() polling for checking
|
120
|
-
* whether the agent is alive.
|
121
|
-
*/
|
122
|
-
ret = pid;
|
123
|
-
status = 0;
|
124
|
-
P_WARN("waitpid() on " << name() << " (pid=" << pid <<
|
125
|
-
") returned -1 with " <<
|
126
|
-
"errno = ECHILD, falling back to kill polling");
|
127
|
-
waitpidUsingKillPolling(pid);
|
128
|
-
e = 0;
|
129
|
-
} else {
|
130
|
-
e = errno;
|
131
|
-
}
|
132
|
-
|
133
|
-
{
|
134
|
-
lock_guard<boost::mutex> l(lock);
|
135
|
-
this->pid = 0;
|
136
|
-
}
|
137
|
-
|
138
|
-
this_thread::disable_interruption di;
|
139
|
-
this_thread::disable_syscall_interruption dsi;
|
140
|
-
if (ret == -1) {
|
141
|
-
P_WARN(name() << " (pid=" << pid << ") crashed or killed for "
|
142
|
-
"an unknown reason (errno = " <<
|
143
|
-
strerror(e) << "), restarting it...");
|
144
|
-
} else if (WIFEXITED(status)) {
|
145
|
-
if (WEXITSTATUS(status) == 0) {
|
146
|
-
/* When the web server is gracefully exiting, it will
|
147
|
-
* tell one or more agents to gracefully exit with exit
|
148
|
-
* status 0. If we see this then it means the watchdog
|
149
|
-
* is gracefully shutting down too and we should stop
|
150
|
-
* watching.
|
151
|
-
*/
|
152
|
-
return;
|
153
|
-
} else {
|
154
|
-
P_WARN(name() << " (pid=" << pid <<
|
155
|
-
") crashed with exit status " <<
|
156
|
-
WEXITSTATUS(status) << ", restarting it...");
|
157
|
-
}
|
158
|
-
} else {
|
159
|
-
P_WARN(name() << " (pid=" << pid <<
|
160
|
-
") crashed with signal " <<
|
161
|
-
getSignalName(WTERMSIG(status)) <<
|
162
|
-
", restarting it...");
|
163
|
-
}
|
164
|
-
|
165
|
-
const char *sleepTime;
|
166
|
-
if ((sleepTime = getenv("PASSENGER_AGENT_RESTART_SLEEP")) != NULL) {
|
167
|
-
sleep(atoi(sleepTime));
|
168
|
-
}
|
169
|
-
}
|
170
|
-
} catch (const boost::thread_interrupted &) {
|
171
|
-
} catch (const tracable_exception &e) {
|
172
|
-
lock_guard<boost::mutex> l(lock);
|
173
|
-
threadExceptionMessage = e.what();
|
174
|
-
threadExceptionBacktrace = e.backtrace();
|
175
|
-
errorEvent->notify();
|
176
|
-
} catch (const std::exception &e) {
|
177
|
-
lock_guard<boost::mutex> l(lock);
|
178
|
-
threadExceptionMessage = e.what();
|
179
|
-
errorEvent->notify();
|
180
|
-
} catch (...) {
|
181
|
-
lock_guard<boost::mutex> l(lock);
|
182
|
-
threadExceptionMessage = "Unknown error";
|
183
|
-
errorEvent->notify();
|
184
|
-
}
|
185
|
-
}
|
186
|
-
|
187
|
-
protected:
|
188
|
-
/** PID of the process we're watching. 0 if no process is started at this time. */
|
189
|
-
pid_t pid;
|
190
|
-
|
191
|
-
/** If the watcher thread threw an uncaught exception then its information will
|
192
|
-
* be stored here so that the main thread can check whether a watcher encountered
|
193
|
-
* an error. These are empty strings if everything is OK.
|
194
|
-
*/
|
195
|
-
string threadExceptionMessage;
|
196
|
-
string threadExceptionBacktrace;
|
197
|
-
|
198
|
-
/** The agent process's feedback fd. */
|
199
|
-
FileDescriptor feedbackFd;
|
200
|
-
|
201
|
-
/**
|
202
|
-
* Lock for protecting the exchange of data between the main thread and
|
203
|
-
* the watcher thread.
|
204
|
-
*/
|
205
|
-
mutable boost::mutex lock;
|
206
|
-
|
207
|
-
/**
|
208
|
-
* Returns the filename of the agent process's executable. This method may be
|
209
|
-
* called in a forked child process and may therefore not allocate memory.
|
210
|
-
*/
|
211
|
-
virtual string getExeFilename() const = 0;
|
212
|
-
|
213
|
-
/**
|
214
|
-
* This method is to exec() the agent with the right arguments.
|
215
|
-
* It is called from within a forked child process, so don't do any dynamic
|
216
|
-
* memory allocations in here. It must also not throw any exceptions.
|
217
|
-
* It must also preserve the value of errno after exec() is called.
|
218
|
-
*/
|
219
|
-
virtual void execProgram() const {
|
220
|
-
execl(getExeFilename().c_str(),
|
221
|
-
getExeFilename().c_str(),
|
222
|
-
"3", // feedback fd
|
223
|
-
(char *) 0);
|
224
|
-
}
|
225
|
-
|
226
|
-
/**
|
227
|
-
* This method is to send startup arguments to the agent process through
|
228
|
-
* the given file descriptor, which is the agent process's feedback fd.
|
229
|
-
* May throw arbitrary exceptions.
|
230
|
-
*/
|
231
|
-
virtual void sendStartupArguments(pid_t pid, FileDescriptor &fd) = 0;
|
232
|
-
|
233
|
-
/**
|
234
|
-
* This method is to process the startup info that the agent process has
|
235
|
-
* sent back. May throw arbitrary exceptions.
|
236
|
-
*/
|
237
|
-
virtual bool processStartupInfo(pid_t pid, FileDescriptor &fd, const vector<string> &args) = 0;
|
238
|
-
|
239
|
-
/**
|
240
|
-
* Kill a process with SIGKILL, and attempt to kill its children too.
|
241
|
-
* Then wait until it has quit.
|
242
|
-
*/
|
243
|
-
static void killAndWait(pid_t pid) {
|
244
|
-
this_thread::disable_interruption di;
|
245
|
-
this_thread::disable_syscall_interruption dsi;
|
246
|
-
// If the process is a process group leader then killing the
|
247
|
-
// group will likely kill all its child processes too.
|
248
|
-
if (syscalls::killpg(pid, SIGKILL) == -1) {
|
249
|
-
syscalls::kill(pid, SIGKILL);
|
250
|
-
}
|
251
|
-
syscalls::waitpid(pid, NULL, 0);
|
252
|
-
}
|
253
|
-
|
254
|
-
/**
|
255
|
-
* Behaves like <tt>waitpid(pid, status, WNOHANG)</tt>, but waits at most
|
256
|
-
* <em>timeout</em> miliseconds for the process to exit.
|
257
|
-
*/
|
258
|
-
static int timedWaitPid(pid_t pid, int *status, unsigned long long timeout) {
|
259
|
-
Timer timer;
|
260
|
-
int ret;
|
261
|
-
|
262
|
-
do {
|
263
|
-
ret = syscalls::waitpid(pid, status, WNOHANG);
|
264
|
-
if (ret > 0 || ret == -1) {
|
265
|
-
return ret;
|
266
|
-
} else {
|
267
|
-
syscalls::usleep(10000);
|
268
|
-
}
|
269
|
-
} while (timer.elapsed() < timeout);
|
270
|
-
return 0; // timed out
|
271
|
-
}
|
272
|
-
|
273
|
-
static void waitpidUsingKillPolling(pid_t pid) {
|
274
|
-
bool done = false;
|
275
|
-
|
276
|
-
while (!done) {
|
277
|
-
int ret = syscalls::kill(pid, 0);
|
278
|
-
done = ret == -1;
|
279
|
-
if (!done) {
|
280
|
-
syscalls::usleep(20000);
|
281
|
-
}
|
282
|
-
}
|
283
|
-
}
|
284
|
-
|
285
|
-
public:
|
286
|
-
AgentWatcher() {
|
287
|
-
thr = NULL;
|
288
|
-
pid = 0;
|
289
|
-
}
|
290
|
-
|
291
|
-
virtual ~AgentWatcher() {
|
292
|
-
delete thr;
|
293
|
-
}
|
294
|
-
|
295
|
-
/**
|
296
|
-
* Send the started agent process's startup information over the given
|
297
|
-
* file descriptor, to the starter process. May throw arbitrary exceptions.
|
298
|
-
*
|
299
|
-
* @pre start() has been called and succeeded.
|
300
|
-
*/
|
301
|
-
virtual void sendStartupInfo(int fd) = 0;
|
302
|
-
|
303
|
-
/** Returns the name of the agent that this class is watching. */
|
304
|
-
virtual const char *name() const = 0;
|
305
|
-
|
306
|
-
/**
|
307
|
-
* Starts the agent process. May throw arbitrary exceptions.
|
308
|
-
*/
|
309
|
-
virtual pid_t start() {
|
310
|
-
this_thread::disable_interruption di;
|
311
|
-
this_thread::disable_syscall_interruption dsi;
|
312
|
-
string exeFilename = getExeFilename();
|
313
|
-
SocketPair fds;
|
314
|
-
int e, ret;
|
315
|
-
pid_t pid;
|
316
|
-
|
317
|
-
/* Create feedback fd for this agent process. We'll send some startup
|
318
|
-
* arguments to this agent process through this fd, and we'll receive
|
319
|
-
* startup information through it as well.
|
320
|
-
*/
|
321
|
-
fds = createUnixSocketPair();
|
322
|
-
|
323
|
-
pid = syscalls::fork();
|
324
|
-
if (pid == 0) {
|
325
|
-
// Child
|
326
|
-
|
327
|
-
/* Make sure file descriptor FEEDBACK_FD refers to the newly created
|
328
|
-
* feedback fd (fds[1]) and close all other file descriptors.
|
329
|
-
* In this child process we don't care about the original FEEDBACK_FD
|
330
|
-
* (which is the Watchdog's communication channel to the agents starter.)
|
331
|
-
*
|
332
|
-
* fds[1] is guaranteed to be != FEEDBACK_FD because the watchdog
|
333
|
-
* is started with FEEDBACK_FD already assigned.
|
334
|
-
*/
|
335
|
-
syscalls::close(fds[0]);
|
336
|
-
|
337
|
-
if (syscalls::dup2(fds[1], FEEDBACK_FD) == -1) {
|
338
|
-
/* Something went wrong, report error through feedback fd. */
|
339
|
-
e = errno;
|
340
|
-
try {
|
341
|
-
writeArrayMessage(fds[1],
|
342
|
-
"system error before exec",
|
343
|
-
"dup2() failed",
|
344
|
-
toString(e).c_str(),
|
345
|
-
NULL);
|
346
|
-
_exit(1);
|
347
|
-
} catch (...) {
|
348
|
-
fprintf(stderr, "Passenger Watchdog: dup2() failed: %s (%d)\n",
|
349
|
-
strerror(e), e);
|
350
|
-
fflush(stderr);
|
351
|
-
_exit(1);
|
352
|
-
}
|
353
|
-
}
|
354
|
-
|
355
|
-
closeAllFileDescriptors(FEEDBACK_FD);
|
356
|
-
|
357
|
-
/* Become the process group leader so that the watchdog can kill the
|
358
|
-
* agent as well as all its descendant processes. */
|
359
|
-
setpgid(getpid(), getpid());
|
360
|
-
|
361
|
-
setOomScore(oldOomScore);
|
362
|
-
|
363
|
-
try {
|
364
|
-
execProgram();
|
365
|
-
} catch (...) {
|
366
|
-
fprintf(stderr, "PassengerWatchdog: execProgram() threw an exception\n");
|
367
|
-
fflush(stderr);
|
368
|
-
_exit(1);
|
369
|
-
}
|
370
|
-
e = errno;
|
371
|
-
try {
|
372
|
-
writeArrayMessage(FEEDBACK_FD,
|
373
|
-
"exec error",
|
374
|
-
toString(e).c_str(),
|
375
|
-
NULL);
|
376
|
-
} catch (...) {
|
377
|
-
fprintf(stderr, "Passenger Watchdog: could not execute %s: %s (%d)\n",
|
378
|
-
exeFilename.c_str(), strerror(e), e);
|
379
|
-
fflush(stderr);
|
380
|
-
}
|
381
|
-
_exit(1);
|
382
|
-
} else if (pid == -1) {
|
383
|
-
// Error
|
384
|
-
e = errno;
|
385
|
-
throw SystemException("Cannot fork a new process", e);
|
386
|
-
} else {
|
387
|
-
// Parent
|
388
|
-
FileDescriptor feedbackFd = fds[0];
|
389
|
-
vector<string> args;
|
390
|
-
|
391
|
-
fds[1].close();
|
392
|
-
this_thread::restore_interruption ri(di);
|
393
|
-
this_thread::restore_syscall_interruption rsi(dsi);
|
394
|
-
ScopeGuard failGuard(boost::bind(killAndWait, pid));
|
395
|
-
|
396
|
-
/* Send startup arguments. Ignore EPIPE and ECONNRESET here
|
397
|
-
* because the child process might have sent an feedback message
|
398
|
-
* without reading startup arguments.
|
399
|
-
*/
|
400
|
-
try {
|
401
|
-
sendStartupArguments(pid, feedbackFd);
|
402
|
-
} catch (const SystemException &ex) {
|
403
|
-
if (ex.code() != EPIPE && ex.code() != ECONNRESET) {
|
404
|
-
throw SystemException(string("Unable to start the ") + name() +
|
405
|
-
": an error occurred while sending startup arguments",
|
406
|
-
ex.code());
|
407
|
-
}
|
408
|
-
}
|
409
|
-
|
410
|
-
// Now read its feedback.
|
411
|
-
try {
|
412
|
-
ret = readArrayMessage(feedbackFd, args);
|
413
|
-
} catch (const SystemException &e) {
|
414
|
-
if (e.code() == ECONNRESET) {
|
415
|
-
ret = false;
|
416
|
-
} else {
|
417
|
-
throw SystemException(string("Unable to start the ") + name() +
|
418
|
-
": unable to read its startup information",
|
419
|
-
e.code());
|
420
|
-
}
|
421
|
-
}
|
422
|
-
if (!ret) {
|
423
|
-
this_thread::disable_interruption di2;
|
424
|
-
this_thread::disable_syscall_interruption dsi2;
|
425
|
-
int status;
|
426
|
-
|
427
|
-
/* The feedback fd was prematurely closed for an unknown reason.
|
428
|
-
* Did the agent process crash?
|
429
|
-
*
|
430
|
-
* We use timedWaitPid() here because if the process crashed
|
431
|
-
* because of an uncaught exception, the file descriptor
|
432
|
-
* might be closed before the process has printed an error
|
433
|
-
* message, so we give it some time to print the error
|
434
|
-
* before we kill it.
|
435
|
-
*/
|
436
|
-
ret = timedWaitPid(pid, &status, 5000);
|
437
|
-
if (ret == 0) {
|
438
|
-
/* Doesn't look like it; it seems it's still running.
|
439
|
-
* We can't do anything without proper feedback so kill
|
440
|
-
* the agent process and throw an exception.
|
441
|
-
*/
|
442
|
-
failGuard.runNow();
|
443
|
-
throw RuntimeException(string("Unable to start the ") + name() +
|
444
|
-
": it froze and reported an unknown error during its startup");
|
445
|
-
} else if (ret != -1 && WIFSIGNALED(status)) {
|
446
|
-
/* Looks like a crash which caused a signal. */
|
447
|
-
throw RuntimeException(string("Unable to start the ") + name() +
|
448
|
-
": it seems to have been killed with signal " +
|
449
|
-
getSignalName(WTERMSIG(status)) + " during startup");
|
450
|
-
} else if (ret == -1) {
|
451
|
-
/* Looks like it exited after detecting an error. */
|
452
|
-
throw RuntimeException(string("Unable to start the ") + name() +
|
453
|
-
": it seems to have crashed during startup for an unknown reason");
|
454
|
-
} else {
|
455
|
-
/* Looks like it exited after detecting an error, but has an exit code. */
|
456
|
-
throw RuntimeException(string("Unable to start the ") + name() +
|
457
|
-
": it seems to have crashed during startup for an unknown reason, "
|
458
|
-
"with exit code " + toString(WEXITSTATUS(status)));
|
459
|
-
}
|
460
|
-
}
|
461
|
-
|
462
|
-
if (args[0] == "system error before exec") {
|
463
|
-
throw SystemException(string("Unable to start the ") + name() +
|
464
|
-
": " + args[1], atoi(args[2]));
|
465
|
-
} else if (args[0] == "exec error") {
|
466
|
-
e = atoi(args[1]);
|
467
|
-
if (e == ENOENT) {
|
468
|
-
throw RuntimeException(string("Unable to start the ") + name() +
|
469
|
-
" because its executable (" + getExeFilename() + ") "
|
470
|
-
"doesn't exist. This probably means that your "
|
471
|
-
"Phusion Passenger installation is broken or "
|
472
|
-
"incomplete. Please reinstall Phusion Passenger");
|
473
|
-
} else {
|
474
|
-
throw SystemException(string("Unable to start the ") + name() +
|
475
|
-
" because exec(\"" + getExeFilename() + "\") failed",
|
476
|
-
atoi(args[1]));
|
477
|
-
}
|
478
|
-
} else if (!processStartupInfo(pid, feedbackFd, args)) {
|
479
|
-
throw RuntimeException(string("The ") + name() +
|
480
|
-
" sent an unknown startup info message '" +
|
481
|
-
args[0] + "'");
|
482
|
-
}
|
483
|
-
|
484
|
-
lock_guard<boost::mutex> l(lock);
|
485
|
-
this->feedbackFd = feedbackFd;
|
486
|
-
this->pid = pid;
|
487
|
-
failGuard.clear();
|
488
|
-
return pid;
|
489
|
-
}
|
490
|
-
}
|
491
|
-
|
492
|
-
/**
|
493
|
-
* Start watching the agent process.
|
494
|
-
*
|
495
|
-
* @pre start() has been called and succeeded.
|
496
|
-
* @pre This watcher isn't already watching.
|
497
|
-
* @throws RuntimeException If a precondition failed.
|
498
|
-
* @throws thread_interrupted
|
499
|
-
* @throws thread_resource_error
|
500
|
-
*/
|
501
|
-
virtual void startWatching() {
|
502
|
-
lock_guard<boost::mutex> l(lock);
|
503
|
-
if (pid == 0) {
|
504
|
-
throw RuntimeException("start() hasn't been called yet");
|
505
|
-
}
|
506
|
-
if (thr != NULL) {
|
507
|
-
throw RuntimeException("Already started watching.");
|
508
|
-
}
|
509
|
-
|
510
|
-
thr = new oxt::thread(boost::bind(&AgentWatcher::threadMain, this, shared_from_this()),
|
511
|
-
name(), 256 * 1024);
|
512
|
-
}
|
513
|
-
|
514
|
-
static void stopWatching(vector< shared_ptr<AgentWatcher> > &watchers) {
|
515
|
-
vector< shared_ptr<AgentWatcher> >::const_iterator it;
|
516
|
-
oxt::thread *threads[watchers.size()];
|
517
|
-
unsigned int i = 0;
|
518
|
-
|
519
|
-
for (it = watchers.begin(); it != watchers.end(); it++, i++) {
|
520
|
-
threads[i] = (*it)->thr;
|
521
|
-
}
|
522
|
-
|
523
|
-
oxt::thread::interrupt_and_join_multiple(threads, watchers.size());
|
524
|
-
for (it = watchers.begin(); it != watchers.end(); it++, i++) {
|
525
|
-
delete (*it)->thr;
|
526
|
-
(*it)->thr = NULL;
|
527
|
-
}
|
528
|
-
}
|
529
|
-
|
530
|
-
/**
|
531
|
-
* Force the agent process to shut down. Returns true if it was shut down,
|
532
|
-
* or false if it wasn't started.
|
533
|
-
*/
|
534
|
-
virtual bool forceShutdown() {
|
535
|
-
lock_guard<boost::mutex> l(lock);
|
536
|
-
if (pid == 0) {
|
537
|
-
return false;
|
538
|
-
} else {
|
539
|
-
killAndWait(pid);
|
540
|
-
this->pid = 0;
|
541
|
-
return true;
|
542
|
-
}
|
543
|
-
}
|
544
|
-
|
545
|
-
/**
|
546
|
-
* If the watcher thread has encountered an error, then the error message
|
547
|
-
* will be stored here. If the error message is empty then it means
|
548
|
-
* everything is still OK.
|
549
|
-
*/
|
550
|
-
string getErrorMessage() const {
|
551
|
-
lock_guard<boost::mutex> l(lock);
|
552
|
-
return threadExceptionMessage;
|
553
|
-
}
|
554
|
-
|
555
|
-
/**
|
556
|
-
* The error backtrace, if applicable.
|
557
|
-
*/
|
558
|
-
string getErrorBacktrace() const {
|
559
|
-
lock_guard<boost::mutex> l(lock);
|
560
|
-
return threadExceptionBacktrace;
|
561
|
-
}
|
562
|
-
|
563
|
-
/**
|
564
|
-
* Returns the agent process feedback fd, or -1 if the agent process
|
565
|
-
* hasn't been started yet. Can be used to check whether this agent process
|
566
|
-
* has exited without using waitpid().
|
567
|
-
*/
|
568
|
-
const FileDescriptor getFeedbackFd() const {
|
569
|
-
lock_guard<boost::mutex> l(lock);
|
570
|
-
return feedbackFd;
|
571
|
-
}
|
572
|
-
};
|
573
|
-
|
574
|
-
typedef shared_ptr<AgentWatcher> AgentWatcherPtr;
|
575
|
-
|
576
|
-
|
577
|
-
class HelperAgentWatcher: public AgentWatcher {
|
578
|
-
protected:
|
579
|
-
string requestSocketFilename;
|
580
|
-
string messageSocketFilename;
|
581
|
-
string helperAgentFilename;
|
582
|
-
string requestSocketPassword;
|
583
|
-
string messageSocketPassword;
|
584
|
-
|
585
|
-
virtual const char *name() const {
|
586
|
-
return "Phusion Passenger helper agent";
|
587
|
-
}
|
588
|
-
|
589
|
-
virtual string getExeFilename() const {
|
590
|
-
return helperAgentFilename;
|
591
|
-
}
|
592
|
-
|
593
|
-
virtual void execProgram() const {
|
594
|
-
if (hasEnvOption("PASSENGER_RUN_HELPER_AGENT_IN_VALGRIND", false)) {
|
595
|
-
execlp("valgrind", "valgrind", "--dsymutil=yes",
|
596
|
-
helperAgentFilename.c_str(), (char *) 0);
|
597
|
-
} else {
|
598
|
-
execl(helperAgentFilename.c_str(), "PassengerHelperAgent", (char *) 0);
|
599
|
-
}
|
600
|
-
}
|
601
|
-
|
602
|
-
virtual void sendStartupArguments(pid_t pid, FileDescriptor &fd) {
|
603
|
-
VariantMap options = agentsOptions;
|
604
|
-
options.set("request_socket_password", Base64::encode(requestSocketPassword)).
|
605
|
-
set("message_socket_password", Base64::encode(messageSocketPassword)).
|
606
|
-
set("logging_agent_address", loggingAgentAddress).
|
607
|
-
set("logging_agent_password", loggingAgentPassword);
|
608
|
-
options.writeToFd(fd);
|
609
|
-
}
|
610
|
-
|
611
|
-
virtual bool processStartupInfo(pid_t pid, FileDescriptor &fd, const vector<string> &args) {
|
612
|
-
if (args[0] == "initialized") {
|
613
|
-
requestSocketFilename = args[1];
|
614
|
-
messageSocketFilename = args[2];
|
615
|
-
return true;
|
616
|
-
} else {
|
617
|
-
return false;
|
618
|
-
}
|
619
|
-
}
|
620
|
-
|
621
|
-
public:
|
622
|
-
HelperAgentWatcher(const ResourceLocator &resourceLocator) {
|
623
|
-
helperAgentFilename = resourceLocator.getAgentsDir() + "/PassengerHelperAgent";
|
624
|
-
requestSocketPassword = randomGenerator->generateByteString(REQUEST_SOCKET_PASSWORD_SIZE);
|
625
|
-
messageSocketPassword = randomGenerator->generateByteString(MESSAGE_SERVER_MAX_PASSWORD_SIZE);
|
626
|
-
}
|
627
|
-
|
628
|
-
virtual void sendStartupInfo(int fd) {
|
629
|
-
writeArrayMessage(fd,
|
630
|
-
"HelperAgent info",
|
631
|
-
requestSocketFilename.c_str(),
|
632
|
-
Base64::encode(requestSocketPassword).c_str(),
|
633
|
-
messageSocketFilename.c_str(),
|
634
|
-
Base64::encode(messageSocketPassword).c_str(),
|
635
|
-
NULL);
|
636
|
-
}
|
637
|
-
};
|
638
99
|
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
virtual const char *name() const {
|
646
|
-
return "Phusion Passenger logging agent";
|
647
|
-
}
|
648
|
-
|
649
|
-
virtual string getExeFilename() const {
|
650
|
-
return agentFilename;
|
651
|
-
}
|
652
|
-
|
653
|
-
virtual void execProgram() const {
|
654
|
-
execl(agentFilename.c_str(), "PassengerLoggingAgent", (char *) 0);
|
655
|
-
}
|
656
|
-
|
657
|
-
virtual void sendStartupArguments(pid_t pid, FileDescriptor &fd) {
|
658
|
-
VariantMap options = agentsOptions;
|
659
|
-
options.set("logging_agent_address", loggingAgentAddress);
|
660
|
-
options.set("logging_agent_password", loggingAgentPassword);
|
661
|
-
options.writeToFd(fd);
|
662
|
-
}
|
663
|
-
|
664
|
-
virtual bool processStartupInfo(pid_t pid, FileDescriptor &fd, const vector<string> &args) {
|
665
|
-
if (args[0] == "initialized") {
|
666
|
-
return true;
|
667
|
-
} else {
|
668
|
-
return false;
|
669
|
-
}
|
670
|
-
}
|
671
|
-
|
672
|
-
public:
|
673
|
-
LoggingAgentWatcher(const ResourceLocator &resourceLocator) {
|
674
|
-
agentFilename = resourceLocator.getAgentsDir() + "/PassengerLoggingAgent";
|
675
|
-
}
|
676
|
-
|
677
|
-
virtual void sendStartupInfo(int fd) {
|
678
|
-
writeArrayMessage(fd,
|
679
|
-
"LoggingServer info",
|
680
|
-
loggingAgentAddress.c_str(),
|
681
|
-
loggingAgentPassword.c_str(),
|
682
|
-
NULL);
|
683
|
-
}
|
684
|
-
};
|
685
|
-
|
686
|
-
|
687
|
-
/**
|
688
|
-
* Touch all files in the server instance dir every 6 hours in order to prevent /tmp
|
689
|
-
* cleaners from weaking havoc:
|
690
|
-
* http://code.google.com/p/phusion-passenger/issues/detail?id=365
|
691
|
-
*/
|
692
|
-
class ServerInstanceDirToucher {
|
693
|
-
private:
|
694
|
-
oxt::thread *thr;
|
695
|
-
|
696
|
-
static void
|
697
|
-
threadMain() {
|
698
|
-
while (!this_thread::interruption_requested()) {
|
699
|
-
syscalls::sleep(60 * 60 * 6);
|
700
|
-
|
701
|
-
begin_touch:
|
702
|
-
|
703
|
-
this_thread::disable_interruption di;
|
704
|
-
this_thread::disable_syscall_interruption dsi;
|
705
|
-
// Fork a process which touches everything in the server instance dir.
|
706
|
-
pid_t pid = syscalls::fork();
|
707
|
-
if (pid == 0) {
|
708
|
-
// Child
|
709
|
-
int prio, ret, e;
|
710
|
-
|
711
|
-
closeAllFileDescriptors(2);
|
712
|
-
|
713
|
-
// Make process nicer.
|
714
|
-
do {
|
715
|
-
prio = getpriority(PRIO_PROCESS, getpid());
|
716
|
-
} while (prio == -1 && errno == EINTR);
|
717
|
-
if (prio != -1) {
|
718
|
-
prio++;
|
719
|
-
if (prio > 20) {
|
720
|
-
prio = 20;
|
721
|
-
}
|
722
|
-
do {
|
723
|
-
ret = setpriority(PRIO_PROCESS, getpid(), prio);
|
724
|
-
} while (ret == -1 && errno == EINTR);
|
725
|
-
} else {
|
726
|
-
perror("getpriority");
|
727
|
-
}
|
728
|
-
|
729
|
-
do {
|
730
|
-
ret = chdir(serverInstanceDir->getPath().c_str());
|
731
|
-
} while (ret == -1 && errno == EINTR);
|
732
|
-
if (ret == -1) {
|
733
|
-
e = errno;
|
734
|
-
fprintf(stderr, "chdir(\"%s\") failed: %s (%d)\n",
|
735
|
-
serverInstanceDir->getPath().c_str(),
|
736
|
-
strerror(e), e);
|
737
|
-
fflush(stderr);
|
738
|
-
_exit(1);
|
739
|
-
}
|
740
|
-
|
741
|
-
setOomScore(oldOomScore);
|
742
|
-
|
743
|
-
execlp("/bin/sh", "/bin/sh", "-c", "find . | xargs touch", (char *) 0);
|
744
|
-
e = errno;
|
745
|
-
fprintf(stderr, "Cannot execute 'find . | xargs touch': %s (%d)\n",
|
746
|
-
strerror(e), e);
|
747
|
-
fflush(stderr);
|
748
|
-
_exit(1);
|
749
|
-
} else if (pid == -1) {
|
750
|
-
// Error
|
751
|
-
P_WARN("Could not touch the server instance directory because "
|
752
|
-
"fork() failed. Retrying in 2 minutes...");
|
753
|
-
this_thread::restore_interruption si(di);
|
754
|
-
this_thread::restore_syscall_interruption rsi(dsi);
|
755
|
-
syscalls::sleep(60 * 2);
|
756
|
-
goto begin_touch;
|
757
|
-
} else {
|
758
|
-
syscalls::waitpid(pid, NULL, 0);
|
759
|
-
}
|
760
|
-
}
|
761
|
-
}
|
762
|
-
|
763
|
-
public:
|
764
|
-
ServerInstanceDirToucher() {
|
765
|
-
thr = new oxt::thread(threadMain, "Server instance dir toucher", 256 * 1024);
|
766
|
-
}
|
767
|
-
|
768
|
-
~ServerInstanceDirToucher() {
|
769
|
-
thr->interrupt_and_join();
|
770
|
-
delete thr;
|
771
|
-
}
|
772
|
-
};
|
100
|
+
#include "AgentWatcher.cpp"
|
101
|
+
#include "HelperAgentWatcher.cpp"
|
102
|
+
#include "LoggingAgentWatcher.cpp"
|
103
|
+
#include "ServerInstanceDirToucher.cpp"
|
104
|
+
static vector<AgentWatcherPtr> watchers;
|
773
105
|
|
774
106
|
|
775
107
|
static bool
|
@@ -790,11 +122,6 @@ hasEnvOption(const char *name, bool defaultValue) {
|
|
790
122
|
}
|
791
123
|
}
|
792
124
|
|
793
|
-
enum OomFileType {
|
794
|
-
OOM_ADJ,
|
795
|
-
OOM_SCORE_ADJ
|
796
|
-
};
|
797
|
-
|
798
125
|
static FILE *
|
799
126
|
openOomAdjFile(const char *mode, OomFileType &type) {
|
800
127
|
FILE *f = fopen("/proc/self/oom_score_adj", mode);
|
@@ -833,6 +160,9 @@ setOomScore(const StaticString &score) {
|
|
833
160
|
}
|
834
161
|
}
|
835
162
|
|
163
|
+
/**
|
164
|
+
* Set the current process's OOM score to "never kill".
|
165
|
+
*/
|
836
166
|
static string
|
837
167
|
setOomScoreNeverKill() {
|
838
168
|
string oldScore;
|
@@ -929,12 +259,12 @@ waitForStarterProcessOrWatchers(vector<AgentWatcherPtr> &watchers) {
|
|
929
259
|
}
|
930
260
|
|
931
261
|
static void
|
932
|
-
cleanupAgentsInBackground(vector<AgentWatcherPtr> &watchers) {
|
262
|
+
cleanupAgentsInBackground(vector<AgentWatcherPtr> &watchers, char *argv[]) {
|
933
263
|
this_thread::disable_interruption di;
|
934
264
|
this_thread::disable_syscall_interruption dsi;
|
935
265
|
pid_t pid;
|
936
266
|
int e;
|
937
|
-
|
267
|
+
|
938
268
|
pid = fork();
|
939
269
|
if (pid == 0) {
|
940
270
|
// Child
|
@@ -943,6 +273,11 @@ cleanupAgentsInBackground(vector<AgentWatcherPtr> &watchers) {
|
|
943
273
|
fd_set fds, fds2;
|
944
274
|
int max, agentProcessesDone;
|
945
275
|
unsigned long long deadline = 30000; // miliseconds
|
276
|
+
|
277
|
+
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(sun)
|
278
|
+
// Change process title.
|
279
|
+
strcpy(argv[0], "PassengerWatchdog (cleaning up...)");
|
280
|
+
#endif
|
946
281
|
|
947
282
|
// Wait until all agent processes have exited. The starter
|
948
283
|
// process is responsible for telling the individual agents
|
@@ -991,8 +326,8 @@ cleanupAgentsInBackground(vector<AgentWatcherPtr> &watchers) {
|
|
991
326
|
P_DEBUG("All Phusion Passenger agent processes have exited. Forcing all subprocesses to shut down.");
|
992
327
|
}
|
993
328
|
for (it = watchers.begin(); it != watchers.end(); it++) {
|
994
|
-
|
995
|
-
|
329
|
+
(*it)->forceShutdown();
|
330
|
+
}
|
996
331
|
|
997
332
|
// Now clean up the server instance directory.
|
998
333
|
delete generation.get();
|
@@ -1044,8 +379,8 @@ inferDefaultGroup(const string &defaultUser) {
|
|
1044
379
|
return groupEntry->gr_name;
|
1045
380
|
}
|
1046
381
|
|
1047
|
-
|
1048
|
-
|
382
|
+
static void
|
383
|
+
initializeBareEssentials(int argc, char *argv[]) {
|
1049
384
|
/*
|
1050
385
|
* Some Apache installations (like on OS X) redirect stdout to /dev/null,
|
1051
386
|
* so that only stderr is redirected to the log file. We therefore
|
@@ -1066,6 +401,11 @@ main(int argc, char *argv[]) {
|
|
1066
401
|
oldOomScore = setOomScoreNeverKill();
|
1067
402
|
|
1068
403
|
agentsOptions = initializeAgent(argc, argv, "PassengerWatchdog");
|
404
|
+
}
|
405
|
+
|
406
|
+
static void
|
407
|
+
initializeOptions() {
|
408
|
+
TRACE_POINT();
|
1069
409
|
agentsOptions
|
1070
410
|
.setDefaultInt ("log_level", DEFAULT_LOG_LEVEL)
|
1071
411
|
.setDefault ("temp_dir", getSystemTempDir())
|
@@ -1080,111 +420,152 @@ main(int argc, char *argv[]) {
|
|
1080
420
|
.setDefaultInt ("max_instances_per_app", DEFAULT_MAX_INSTANCES_PER_APP)
|
1081
421
|
.setDefaultInt ("pool_idle_time", DEFAULT_POOL_IDLE_TIME);
|
1082
422
|
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
// Required options
|
1088
|
-
passengerRoot = agentsOptions.get("passenger_root");
|
1089
|
-
webServerPid = agentsOptions.getPid("web_server_pid");
|
423
|
+
// Check for required options
|
424
|
+
UPDATE_TRACE_POINT();
|
425
|
+
agentsOptions.get("passenger_root");
|
426
|
+
agentsOptions.getPid("web_server_pid");
|
1090
427
|
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
428
|
+
// Fetch optional options
|
429
|
+
UPDATE_TRACE_POINT();
|
430
|
+
tempDir = agentsOptions.get("temp_dir");
|
431
|
+
userSwitching = agentsOptions.getBool("user_switching");
|
432
|
+
defaultUser = agentsOptions.get("default_user");
|
433
|
+
if (!agentsOptions.has("default_group")) {
|
434
|
+
agentsOptions.set("default_group", inferDefaultGroup(defaultUser));
|
435
|
+
}
|
436
|
+
defaultGroup = agentsOptions.get("default_group");
|
437
|
+
webServerWorkerUid = agentsOptions.getUid("web_server_worker_uid");
|
438
|
+
webServerWorkerGid = agentsOptions.getGid("web_server_worker_gid");
|
1102
439
|
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
440
|
+
P_INFO("Options: " << agentsOptions.inspect());
|
441
|
+
}
|
442
|
+
|
443
|
+
static void
|
444
|
+
initializeWorkingObjects() {
|
445
|
+
TRACE_POINT();
|
446
|
+
randomGenerator = new RandomGenerator();
|
447
|
+
errorEvent = new EventFd();
|
448
|
+
resourceLocator = new ResourceLocator(agentsOptions.get("passenger_root"));
|
449
|
+
|
450
|
+
UPDATE_TRACE_POINT();
|
451
|
+
// Must not used make_shared() here because Watchdog.cpp
|
452
|
+
// deletes the raw pointer in cleanupAgentsInBackground().
|
453
|
+
if (agentsOptions.get("server_instance_dir", false).empty()) {
|
454
|
+
/* We embed the super structure version in the server instance directory name
|
455
|
+
* because it's possible to upgrade Phusion Passenger without changing the
|
456
|
+
* web server's PID. This way each incompatible upgrade will use its own
|
457
|
+
* server instance directory.
|
458
|
+
*/
|
459
|
+
string path = tempDir +
|
460
|
+
"/passenger." +
|
461
|
+
toString(ServerInstanceDir::DIR_STRUCTURE_MAJOR_VERSION) + "." +
|
462
|
+
toString(ServerInstanceDir::DIR_STRUCTURE_MINOR_VERSION) + "." +
|
463
|
+
toString<unsigned long long>(agentsOptions.getPid("web_server_pid"));
|
464
|
+
serverInstanceDir.reset(new ServerInstanceDir(path));
|
465
|
+
} else {
|
466
|
+
serverInstanceDir.reset(new ServerInstanceDir(agentsOptions.get("server_instance_dir")));
|
1111
467
|
agentsOptions.set("server_instance_dir", serverInstanceDir->getPath());
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
"Watchdog startup error",
|
1147
|
-
e.what(),
|
1148
|
-
NULL);
|
1149
|
-
forceAllAgentsShutdown(watchers);
|
1150
|
-
return 1;
|
1151
|
-
}
|
1152
|
-
// Allow other exceptions to propagate and crash the watchdog.
|
468
|
+
}
|
469
|
+
generation = serverInstanceDir->newGeneration(userSwitching, defaultUser,
|
470
|
+
defaultGroup, webServerWorkerUid, webServerWorkerGid);
|
471
|
+
agentsOptions.set("server_instance_dir", serverInstanceDir->getPath());
|
472
|
+
agentsOptions.setInt("generation_number", generation->getNumber());
|
473
|
+
|
474
|
+
UPDATE_TRACE_POINT();
|
475
|
+
serverInstanceDirToucher = new ServerInstanceDirToucher();
|
476
|
+
|
477
|
+
UPDATE_TRACE_POINT();
|
478
|
+
loggingAgentAddress = "unix:" + generation->getPath() + "/logging";
|
479
|
+
loggingAgentPassword = randomGenerator->generateAsciiString(64);
|
480
|
+
}
|
481
|
+
|
482
|
+
static void
|
483
|
+
initializeAgentWatchers() {
|
484
|
+
TRACE_POINT();
|
485
|
+
watchers.push_back(make_shared<HelperAgentWatcher>(*resourceLocator));
|
486
|
+
watchers.push_back(make_shared<LoggingAgentWatcher>(*resourceLocator));
|
487
|
+
}
|
488
|
+
|
489
|
+
static void
|
490
|
+
startAgents() {
|
491
|
+
TRACE_POINT();
|
492
|
+
foreach (AgentWatcherPtr watcher, watchers) {
|
493
|
+
try {
|
494
|
+
watcher->start();
|
495
|
+
} catch (const std::exception &e) {
|
496
|
+
writeArrayMessage(FEEDBACK_FD,
|
497
|
+
"Watchdog startup error",
|
498
|
+
e.what(),
|
499
|
+
NULL);
|
500
|
+
forceAllAgentsShutdown(watchers);
|
501
|
+
exit(1);
|
1153
502
|
}
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
503
|
+
// Allow other exceptions to propagate and crash the watchdog.
|
504
|
+
}
|
505
|
+
}
|
506
|
+
|
507
|
+
static void
|
508
|
+
startWatchingAgents() {
|
509
|
+
foreach (AgentWatcherPtr watcher, watchers) {
|
510
|
+
try {
|
511
|
+
watcher->startWatching();
|
512
|
+
} catch (const std::exception &e) {
|
513
|
+
writeArrayMessage(FEEDBACK_FD,
|
514
|
+
"Watchdog startup error",
|
515
|
+
e.what(),
|
516
|
+
NULL);
|
517
|
+
forceAllAgentsShutdown(watchers);
|
518
|
+
exit(1);
|
1167
519
|
}
|
1168
|
-
|
1169
|
-
|
520
|
+
// Allow other exceptions to propagate and crash the watchdog.
|
521
|
+
}
|
522
|
+
}
|
523
|
+
|
524
|
+
static void
|
525
|
+
reportAgentsInformation() {
|
526
|
+
TRACE_POINT();
|
527
|
+
VariantMap report;
|
528
|
+
|
529
|
+
report
|
530
|
+
.set("server_instance_dir", serverInstanceDir->getPath())
|
531
|
+
.setInt("generation", generation->getNumber());
|
532
|
+
|
533
|
+
foreach (AgentWatcherPtr watcher, watchers) {
|
534
|
+
watcher->reportAgentsInformation(report);
|
535
|
+
}
|
536
|
+
|
537
|
+
report.writeToFd(FEEDBACK_FD, "Agents information");
|
538
|
+
}
|
539
|
+
|
540
|
+
int
|
541
|
+
main(int argc, char *argv[]) {
|
542
|
+
initializeBareEssentials(argc, argv);
|
543
|
+
P_DEBUG("Starting Watchdog...");
|
544
|
+
|
545
|
+
try {
|
546
|
+
TRACE_POINT();
|
547
|
+
initializeOptions();
|
548
|
+
initializeWorkingObjects();
|
549
|
+
initializeAgentWatchers();
|
550
|
+
} catch (const std::exception &e) {
|
1170
551
|
writeArrayMessage(FEEDBACK_FD,
|
1171
|
-
"
|
1172
|
-
|
1173
|
-
toString(generation->getNumber()).c_str(),
|
552
|
+
"Watchdog startup error",
|
553
|
+
e.what(),
|
1174
554
|
NULL);
|
555
|
+
return 1;
|
556
|
+
}
|
557
|
+
// Allow other exceptions to propagate and crash the watchdog.
|
558
|
+
|
559
|
+
try {
|
560
|
+
TRACE_POINT();
|
561
|
+
startAgents();
|
562
|
+
startWatchingAgents();
|
563
|
+
reportAgentsInformation();
|
564
|
+
P_INFO("All Phusion Passenger agents started!");
|
1175
565
|
|
1176
566
|
UPDATE_TRACE_POINT();
|
1177
|
-
for (it = watchers.begin(); it != watchers.end(); it++) {
|
1178
|
-
(*it)->sendStartupInfo(FEEDBACK_FD);
|
1179
|
-
}
|
1180
|
-
|
1181
|
-
UPDATE_TRACE_POINT();
|
1182
|
-
writeArrayMessage(FEEDBACK_FD, "All agents started", NULL);
|
1183
|
-
P_DEBUG("All Phusion Passenger agents started!");
|
1184
|
-
|
1185
567
|
this_thread::disable_interruption di;
|
1186
568
|
this_thread::disable_syscall_interruption dsi;
|
1187
|
-
UPDATE_TRACE_POINT();
|
1188
569
|
bool exitGracefully = waitForStarterProcessOrWatchers(watchers);
|
1189
570
|
if (exitGracefully) {
|
1190
571
|
/* Fork a child process which cleans up all the agent processes in
|
@@ -1199,7 +580,7 @@ main(int argc, char *argv[]) {
|
|
1199
580
|
AgentWatcher::stopWatching(watchers);
|
1200
581
|
if (exitGracefully) {
|
1201
582
|
UPDATE_TRACE_POINT();
|
1202
|
-
cleanupAgentsInBackground(watchers);
|
583
|
+
cleanupAgentsInBackground(watchers, argv);
|
1203
584
|
return 0;
|
1204
585
|
} else {
|
1205
586
|
UPDATE_TRACE_POINT();
|
@@ -1209,8 +590,5 @@ main(int argc, char *argv[]) {
|
|
1209
590
|
} catch (const tracable_exception &e) {
|
1210
591
|
P_ERROR(e.what() << "\n" << e.backtrace());
|
1211
592
|
return 1;
|
1212
|
-
} catch (const std::exception &e) {
|
1213
|
-
P_ERROR(e.what());
|
1214
|
-
return 1;
|
1215
593
|
}
|
1216
594
|
}
|