passenger 3.9.2.beta → 4.0.0.rc4
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/.travis.yml +3 -0
- data/NEWS +77 -7
- data/README.md +3 -11
- data/bin/passenger-install-apache2-module +24 -20
- data/bin/passenger-install-nginx-module +25 -23
- data/build/agents.rb +11 -0
- data/build/apache2.rb +9 -5
- data/build/basics.rb +37 -30
- data/build/common_library.rb +4 -1
- data/build/cplusplus_support.rb +5 -5
- data/build/cxx_tests.rb +28 -8
- data/build/integration_tests.rb +6 -3
- data/build/nginx.rb +3 -3
- data/build/packaging.rb +95 -57
- data/build/ruby_extension.rb +34 -21
- data/build/ruby_tests.rb +4 -2
- data/build/test_basics.rb +1 -1
- data/dev/run_travis.sh +36 -1
- data/doc/Users guide Apache.html +425 -308
- data/doc/Users guide Apache.idmap.txt +78 -70
- data/doc/Users guide Apache.index.sqlite3 +0 -0
- data/doc/Users guide Apache.txt +33 -92
- data/doc/Users guide Nginx.html +519 -220
- data/doc/Users guide Nginx.idmap.txt +78 -60
- data/doc/Users guide Nginx.txt +115 -26
- data/doc/Users guide Standalone.html +8 -2
- data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +1 -7
- data/doc/users_guide_snippets/installation.txt +167 -22
- data/doc/users_guide_snippets/rackup_specifications.txt +4 -0
- data/doc/users_guide_snippets/since_version.txt +1 -0
- data/doc/users_guide_snippets/support_information.txt +3 -7
- data/doc/users_guide_snippets/tips.txt +0 -24
- data/ext/apache2/Configuration.cpp +11 -33
- data/ext/apache2/Configuration.hpp +3 -18
- data/ext/apache2/DirectoryMapper.h +20 -70
- data/ext/apache2/Hooks.cpp +2 -2
- data/ext/common/AgentsStarter.cpp +0 -2
- data/ext/common/AgentsStarter.h +0 -1
- data/ext/common/AgentsStarter.hpp +1 -3
- data/ext/common/ApplicationPool2/AppTypes.cpp +74 -0
- data/ext/common/ApplicationPool2/AppTypes.h +202 -0
- data/ext/common/ApplicationPool2/Common.h +12 -10
- data/ext/common/ApplicationPool2/DirectSpawner.h +256 -0
- data/ext/common/ApplicationPool2/DummySpawner.h +90 -0
- data/ext/common/ApplicationPool2/Group.h +311 -94
- data/ext/common/ApplicationPool2/Implementation.cpp +405 -145
- data/ext/common/ApplicationPool2/Options.h +24 -26
- data/ext/common/ApplicationPool2/PipeWatcher.h +20 -13
- data/ext/common/ApplicationPool2/Pool.h +326 -183
- data/ext/common/ApplicationPool2/Process.h +205 -55
- data/ext/common/ApplicationPool2/README.md +1 -1
- data/ext/common/ApplicationPool2/Session.h +21 -10
- data/ext/common/ApplicationPool2/SmartSpawner.h +801 -0
- data/ext/common/ApplicationPool2/Spawner.h +141 -1149
- data/ext/common/ApplicationPool2/SpawnerFactory.h +132 -0
- data/ext/common/ApplicationPool2/SuperGroup.h +146 -223
- data/ext/common/Constants.h +4 -2
- data/ext/common/Exceptions.h +23 -1
- data/ext/common/Logging.cpp +17 -6
- data/ext/common/Logging.h +37 -7
- data/ext/common/ResourceLocator.h +1 -1
- data/ext/common/Utils.cpp +49 -1
- data/ext/common/Utils.h +13 -4
- data/ext/common/{AnsiColorConstants.h → Utils/AnsiColorConstants.h} +0 -0
- data/ext/common/{BCrypt.cpp → Utils/BCrypt.cpp} +0 -0
- data/ext/common/{BCrypt.h → Utils/BCrypt.h} +0 -0
- data/ext/common/{Blowfish.c → Utils/Blowfish.c} +0 -0
- data/ext/common/{Blowfish.h → Utils/Blowfish.h} +0 -0
- data/ext/common/Utils/CachedFileStat.hpp +27 -25
- data/ext/common/Utils/Curl.h +184 -0
- data/ext/common/{HttpConstants.h → Utils/HttpConstants.h} +3 -0
- data/ext/common/Utils/IOUtils.cpp +6 -2
- data/ext/common/{IniFile.h → Utils/IniFile.h} +0 -0
- data/ext/common/Utils/LargeFiles.cpp +30 -0
- data/ext/common/Utils/LargeFiles.h +40 -0
- data/ext/common/Utils/StrIntUtils.cpp +72 -8
- data/ext/common/Utils/StrIntUtils.h +24 -2
- data/ext/common/Utils/StringMap.h +12 -2
- data/ext/common/Utils/VariantMap.h +51 -2
- data/ext/common/Utils/jsoncpp.cpp +1 -1
- data/ext/common/agents/Base.cpp +147 -11
- data/ext/common/agents/HelperAgent/AgentOptions.h +14 -6
- data/ext/common/agents/HelperAgent/Main.cpp +79 -19
- data/ext/common/agents/HelperAgent/RequestHandler.h +36 -16
- data/ext/common/agents/LoggingAgent/LoggingServer.h +3 -5
- data/ext/common/agents/LoggingAgent/Main.cpp +2 -4
- data/ext/common/agents/LoggingAgent/RemoteSender.h +18 -24
- data/ext/common/agents/SpawnPreparer.cpp +7 -0
- data/ext/common/agents/Watchdog/Main.cpp +96 -38
- data/ext/nginx/Configuration.c +26 -22
- data/ext/nginx/Configuration.h +4 -2
- data/ext/nginx/ContentHandler.c +23 -52
- data/ext/nginx/ContentHandler.h +5 -11
- data/ext/nginx/config +10 -3
- data/ext/nginx/ngx_http_passenger_module.c +21 -6
- data/ext/nginx/ngx_http_passenger_module.h +4 -1
- data/ext/oxt/dynamic_thread_group.hpp +9 -1
- data/ext/oxt/system_calls.cpp +2 -2
- data/ext/ruby/extconf.rb +2 -1
- data/helper-scripts/backtrace-sanitizer.rb +2 -0
- data/helper-scripts/wsgi-loader.py +54 -21
- data/lib/phusion_passenger.rb +5 -3
- data/lib/phusion_passenger/abstract_installer.rb +18 -41
- data/lib/phusion_passenger/admin_tools/memory_stats.rb +2 -2
- data/lib/phusion_passenger/admin_tools/server_instance.rb +2 -2
- data/lib/phusion_passenger/common_library.rb +23 -3
- data/lib/phusion_passenger/debug_logging.rb +10 -3
- data/lib/phusion_passenger/packaging.rb +1 -0
- data/lib/phusion_passenger/platform_info.rb +113 -115
- data/lib/phusion_passenger/platform_info/compiler.rb +224 -134
- data/lib/phusion_passenger/platform_info/cxx_portability.rb +143 -0
- data/lib/phusion_passenger/platform_info/depcheck.rb +371 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +124 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +97 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +39 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +118 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +137 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +15 -0
- data/lib/phusion_passenger/platform_info/operating_system.rb +6 -5
- data/lib/phusion_passenger/platform_info/ruby.rb +45 -34
- data/lib/phusion_passenger/request_handler.rb +35 -22
- data/lib/phusion_passenger/request_handler/thread_handler.rb +5 -6
- data/lib/phusion_passenger/ruby_core_enhancements.rb +7 -1
- data/lib/phusion_passenger/standalone/runtime_installer.rb +43 -34
- data/lib/phusion_passenger/utils/robust_interruption.rb +34 -18
- data/passenger.gemspec +25 -0
- data/resources/templates/standalone/config.erb +3 -1
- data/test/config.json.travis +2 -2
- data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +37 -5
- data/test/cxx/ApplicationPool2/PoolTest.cpp +143 -50
- data/test/cxx/ApplicationPool2/ProcessTest.cpp +8 -0
- data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +28 -17
- data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +31 -26
- data/test/cxx/RequestHandlerTest.cpp +17 -1
- data/test/cxx/UtilsTest.cpp +84 -10
- data/test/integration_tests/apache2_tests.rb +49 -163
- data/test/integration_tests/hello_world_wsgi_spec.rb +2 -2
- data/test/integration_tests/mycook_spec.rb +1 -1
- data/test/integration_tests/nginx_tests.rb +37 -19
- data/test/ruby/request_handler_spec.rb +1 -0
- data/test/ruby/spec_helper.rb +52 -1
- data/test/stub/nginx/nginx.conf.erb +2 -0
- data/test/stub/rack/start.rb +5 -0
- data/test/stub/rails3.0/Gemfile.lock +30 -30
- data/test/stub/rails3.1/Gemfile +1 -1
- data/test/stub/rails3.1/Gemfile.lock +3 -3
- data/test/stub/rails3.2/Gemfile +1 -1
- data/test/stub/rails3.2/Gemfile.lock +4 -4
- data/test/stub/rails_apps/2.3/mycook/app/controllers/welcome_controller.rb +1 -1
- data/test/stub/rails_apps/2.3/mycook/app/helpers/recipes_helper.rb +2 -0
- data/test/stub/rails_apps/2.3/mycook/app/helpers/test_helper.rb +2 -0
- data/test/stub/rails_apps/2.3/mycook/app/helpers/uploads_helper.rb +2 -0
- data/test/stub/rails_apps/2.3/mycook/app/helpers/welcome_helper.rb +2 -0
- data/test/support/nginx_controller.rb +2 -1
- metadata +160 -156
- data/build/gempackagetask.rb +0 -99
- data/build/packagetask.rb +0 -186
- data/ext/common/StringListCreator.h +0 -83
- data/lib/phusion_passenger/dependencies.rb +0 -657
@@ -60,6 +60,7 @@
|
|
60
60
|
#include <boost/make_shared.hpp>
|
61
61
|
#include <boost/shared_array.hpp>
|
62
62
|
#include <boost/bind.hpp>
|
63
|
+
#include <boost/foreach.hpp>
|
63
64
|
#include <oxt/system_calls.hpp>
|
64
65
|
#include <oxt/backtrace.hpp>
|
65
66
|
#include <sys/types.h>
|
@@ -69,8 +70,6 @@
|
|
69
70
|
#include <cerrno>
|
70
71
|
#include <cassert>
|
71
72
|
#include <unistd.h>
|
72
|
-
#include <pthread.h>
|
73
|
-
#include <limits.h> // for PTHREAD_STACK_MIN
|
74
73
|
#include <pwd.h>
|
75
74
|
#include <grp.h>
|
76
75
|
#include <dirent.h>
|
@@ -83,6 +82,7 @@
|
|
83
82
|
#include <ResourceLocator.h>
|
84
83
|
#include <StaticString.h>
|
85
84
|
#include <ServerInstanceDir.h>
|
85
|
+
#include <Utils.h>
|
86
86
|
#include <Utils/BufferedIO.h>
|
87
87
|
#include <Utils/ScopeGuard.h>
|
88
88
|
#include <Utils/Timer.h>
|
@@ -117,7 +117,8 @@ protected:
|
|
117
117
|
class BackgroundIOCapturer {
|
118
118
|
private:
|
119
119
|
FileDescriptor fd;
|
120
|
-
|
120
|
+
string prefix;
|
121
|
+
bool print;
|
121
122
|
boost::mutex dataSyncher;
|
122
123
|
string data;
|
123
124
|
oxt::thread *thr;
|
@@ -145,18 +146,28 @@ protected:
|
|
145
146
|
lock_guard<boost::mutex> l(dataSyncher);
|
146
147
|
data.append(buf, ret);
|
147
148
|
}
|
148
|
-
|
149
|
-
|
150
|
-
|
149
|
+
UPDATE_TRACE_POINT();
|
150
|
+
if (print && ret == 1 && buf[0] == '\n') {
|
151
|
+
P_INFO(prefix);
|
152
|
+
} else if (print) {
|
153
|
+
vector<StaticString> lines;
|
154
|
+
if (ret > 0 && buf[ret - 1] == '\n') {
|
155
|
+
ret--;
|
156
|
+
}
|
157
|
+
split(StaticString(buf, ret), '\n', lines);
|
158
|
+
foreach (const StaticString line, lines) {
|
159
|
+
P_INFO(prefix << line);
|
160
|
+
}
|
151
161
|
}
|
152
162
|
}
|
153
163
|
}
|
154
164
|
}
|
155
165
|
|
156
166
|
public:
|
157
|
-
BackgroundIOCapturer(const FileDescriptor &_fd,
|
167
|
+
BackgroundIOCapturer(const FileDescriptor &_fd, const string &_prefix, bool _print)
|
158
168
|
: fd(_fd),
|
159
|
-
|
169
|
+
prefix(_prefix),
|
170
|
+
print(_print),
|
160
171
|
thr(NULL)
|
161
172
|
{ }
|
162
173
|
|
@@ -257,6 +268,12 @@ protected:
|
|
257
268
|
|
258
269
|
typedef shared_ptr<DebugDir> DebugDirPtr;
|
259
270
|
|
271
|
+
/**
|
272
|
+
* Contains information that will be used after fork()ing but before exec()ing,
|
273
|
+
* such as the intended app root, the UID it should switch to, the
|
274
|
+
* groups it should assume, etc. This structure is allocated before forking
|
275
|
+
* because after forking and before exec() it may not be safe to allocate memory.
|
276
|
+
*/
|
260
277
|
struct SpawnPreparationInfo {
|
261
278
|
// General
|
262
279
|
|
@@ -302,9 +319,21 @@ protected:
|
|
302
319
|
* the spawning protocol.
|
303
320
|
*/
|
304
321
|
struct NegotiationDetails {
|
305
|
-
|
322
|
+
/****** Arguments ******/
|
323
|
+
|
324
|
+
/** The preparation info of the process we're negotiating with. It's used
|
325
|
+
* by security validators to check whether the information sent back by the
|
326
|
+
* process make any sense. */
|
327
|
+
SpawnPreparationInfo *preparation;
|
328
|
+
/** The SafeLibev that the returned Process should be initialized with. */
|
306
329
|
SafeLibevPtr libev;
|
330
|
+
/** This object captures the process's stderr while negotiation is in progress.
|
331
|
+
* (Recall that negotiation is performed over the process's stdout while stderr
|
332
|
+
* is used purely for outputting messages.)
|
333
|
+
* If the negotiation protocol fails, then any output captured by this object
|
334
|
+
* will be stored into the resulting SpawnException's error page. */
|
307
335
|
BackgroundIOCapturerPtr stderrCapturer;
|
336
|
+
/** The PID of the process we're negotiating with. */
|
308
337
|
pid_t pid;
|
309
338
|
FileDescriptor adminSocket;
|
310
339
|
FileDescriptor errorPipe;
|
@@ -313,7 +342,7 @@ protected:
|
|
313
342
|
int forwardStderrTo;
|
314
343
|
DebugDirPtr debugDir;
|
315
344
|
|
316
|
-
|
345
|
+
/****** Working state ******/
|
317
346
|
BufferedIO io;
|
318
347
|
string gupid;
|
319
348
|
string connectPassword;
|
@@ -321,6 +350,7 @@ protected:
|
|
321
350
|
unsigned long long timeout;
|
322
351
|
|
323
352
|
NegotiationDetails() {
|
353
|
+
preparation = NULL;
|
324
354
|
pid = 0;
|
325
355
|
options = NULL;
|
326
356
|
forwardStderr = false;
|
@@ -329,31 +359,6 @@ protected:
|
|
329
359
|
timeout = 0;
|
330
360
|
}
|
331
361
|
};
|
332
|
-
|
333
|
-
/**
|
334
|
-
* Structure containing arguments and working state for negotiating
|
335
|
-
* the preloader startup protocol.
|
336
|
-
*/
|
337
|
-
struct StartupDetails {
|
338
|
-
// Arguments.
|
339
|
-
FileDescriptor adminSocket;
|
340
|
-
BufferedIO io;
|
341
|
-
BackgroundIOCapturerPtr stderrCapturer;
|
342
|
-
DebugDirPtr debugDir;
|
343
|
-
const Options *options;
|
344
|
-
bool forwardStderr;
|
345
|
-
int forwardStderrTo;
|
346
|
-
|
347
|
-
// Working state.
|
348
|
-
unsigned long long timeout;
|
349
|
-
|
350
|
-
StartupDetails() {
|
351
|
-
options = NULL;
|
352
|
-
forwardStderr = false;
|
353
|
-
forwardStderrTo = STDERR_FILENO;
|
354
|
-
timeout = 0;
|
355
|
-
}
|
356
|
-
};
|
357
362
|
|
358
363
|
|
359
364
|
private:
|
@@ -394,8 +399,12 @@ private:
|
|
394
399
|
data.append(key + ": " + value + "\n");
|
395
400
|
}
|
396
401
|
|
402
|
+
vector<StaticString> lines;
|
403
|
+
split(data, '\n', lines);
|
404
|
+
foreach (const StaticString line, lines) {
|
405
|
+
P_DEBUG("[App " << details.pid << " stdin >>] " << line);
|
406
|
+
}
|
397
407
|
writeExact(details.adminSocket, data, &details.timeout);
|
398
|
-
P_TRACE(2, "Spawn request for " << details.options->appRoot << ":\n" << data);
|
399
408
|
writeExact(details.adminSocket, "\n", &details.timeout);
|
400
409
|
} catch (const SystemException &e) {
|
401
410
|
if (e.code() == EPIPE) {
|
@@ -465,6 +474,13 @@ private:
|
|
465
474
|
vector<string> args;
|
466
475
|
split(value, ';', args);
|
467
476
|
if (args.size() == 4) {
|
477
|
+
string error = validateSocketAddress(details, args[1]);
|
478
|
+
if (!error.empty()) {
|
479
|
+
throwAppSpawnException(
|
480
|
+
"An error occurred while starting the web application. " + error,
|
481
|
+
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
482
|
+
details);
|
483
|
+
}
|
468
484
|
sockets->add(args[0],
|
469
485
|
fixupSocketAddress(*details.options, args[1]),
|
470
486
|
args[2],
|
@@ -501,7 +517,6 @@ private:
|
|
501
517
|
|
502
518
|
protected:
|
503
519
|
ResourceLocator resourceLocator;
|
504
|
-
RandomGeneratorPtr randomGenerator;
|
505
520
|
ServerInstanceDir::GenerationPtr generation;
|
506
521
|
SpawnerConfigPtr config;
|
507
522
|
|
@@ -555,6 +570,73 @@ protected:
|
|
555
570
|
}
|
556
571
|
}
|
557
572
|
|
573
|
+
bool isAbsolutePath(const StaticString &path) const {
|
574
|
+
if (path.empty() || path[0] != '/') {
|
575
|
+
return false;
|
576
|
+
} else {
|
577
|
+
vector<string> components;
|
578
|
+
string component;
|
579
|
+
|
580
|
+
split(path, '/', components);
|
581
|
+
components.erase(components.begin());
|
582
|
+
foreach (component, components) {
|
583
|
+
if (component.empty() || component == "." || component == "..") {
|
584
|
+
return false;
|
585
|
+
}
|
586
|
+
}
|
587
|
+
return true;
|
588
|
+
}
|
589
|
+
}
|
590
|
+
|
591
|
+
/**
|
592
|
+
* Given a 'socket:' information string obtained from the spawned process,
|
593
|
+
* validates whether it is correct.
|
594
|
+
*/
|
595
|
+
string validateSocketAddress(NegotiationDetails &details, const string &_address) const {
|
596
|
+
string address = _address;
|
597
|
+
stringstream error;
|
598
|
+
|
599
|
+
switch (getSocketAddressType(address)) {
|
600
|
+
case SAT_UNIX: {
|
601
|
+
address = fixupSocketAddress(*details.options, address);
|
602
|
+
string filename = parseUnixSocketAddress(address);
|
603
|
+
|
604
|
+
// Verify that the socket filename is absolute.
|
605
|
+
if (!isAbsolutePath(filename)) {
|
606
|
+
error << "It reported a non-absolute socket filename: \"" <<
|
607
|
+
cEscapeString(filename) << "\"";
|
608
|
+
break;
|
609
|
+
}
|
610
|
+
|
611
|
+
// Verify that the process owns the socket.
|
612
|
+
struct stat buf;
|
613
|
+
if (lstat(filename.c_str(), &buf) == -1) {
|
614
|
+
int e = errno;
|
615
|
+
error << "It reported an inaccessible socket filename: \"" <<
|
616
|
+
cEscapeString(filename) << "\" (lstat() failed with errno " <<
|
617
|
+
e << ": " << strerror(e) << ")";
|
618
|
+
break;
|
619
|
+
}
|
620
|
+
if (buf.st_uid != details.preparation->uid) {
|
621
|
+
error << "It advertised a Unix domain socket that has a different " <<
|
622
|
+
"owner than expected (should be UID " << details.preparation->uid <<
|
623
|
+
", but actual UID was " << buf.st_uid << ")";
|
624
|
+
break;
|
625
|
+
}
|
626
|
+
break;
|
627
|
+
}
|
628
|
+
case SAT_TCP:
|
629
|
+
// TODO: validate that the socket is localhost.
|
630
|
+
break;
|
631
|
+
default:
|
632
|
+
error << "It reported an unsupported socket address type: \"" <<
|
633
|
+
cEscapeString(address) << "\"";
|
634
|
+
break;
|
635
|
+
}
|
636
|
+
|
637
|
+
return error.str();
|
638
|
+
}
|
639
|
+
|
558
640
|
static void checkChrootDirectories(const Options &options) {
|
559
641
|
if (!options.preexecChroot.empty()) {
|
560
642
|
// TODO: check whether appRoot is a child directory of preexecChroot
|
@@ -633,22 +715,28 @@ protected:
|
|
633
715
|
}
|
634
716
|
|
635
717
|
template<typename Details>
|
636
|
-
|
718
|
+
string readMessageLine(Details &details) {
|
637
719
|
TRACE_POINT();
|
638
720
|
while (true) {
|
639
721
|
string result = details.io.readLine(1024 * 4, &details.timeout);
|
722
|
+
string line = result;
|
723
|
+
if (!line.empty() && line[line.size() - 1] == '\n') {
|
724
|
+
line.erase(line.size() - 1, 1);
|
725
|
+
}
|
726
|
+
|
640
727
|
if (result.empty()) {
|
728
|
+
// EOF
|
641
729
|
return result;
|
642
730
|
} else if (startsWith(result, "!> ")) {
|
731
|
+
P_DEBUG("[App " << details.pid << " stdout] " << line);
|
643
732
|
result.erase(0, sizeof("!> ") - 1);
|
644
733
|
return result;
|
645
734
|
} else {
|
646
735
|
if (details.stderrCapturer != NULL) {
|
647
736
|
details.stderrCapturer->appendToBuffer(result);
|
648
737
|
}
|
649
|
-
|
650
|
-
|
651
|
-
}
|
738
|
+
P_LOG(config->forwardStdout ? LVL_INFO : LVL_DEBUG,
|
739
|
+
"[App " << details.pid << " stdout] " << line);
|
652
740
|
}
|
653
741
|
}
|
654
742
|
}
|
@@ -783,7 +871,6 @@ protected:
|
|
783
871
|
gid_t groups[1024];
|
784
872
|
info.ngroups = sizeof(groups) / sizeof(gid_t);
|
785
873
|
#endif
|
786
|
-
int ret;
|
787
874
|
info.switchUser = true;
|
788
875
|
info.username = userInfo->pw_name;
|
789
876
|
info.groupname = groupInfo->gr_name;
|
@@ -795,7 +882,7 @@ protected:
|
|
795
882
|
#define HAVE_GETGROUPLIST
|
796
883
|
#endif
|
797
884
|
#ifdef HAVE_GETGROUPLIST
|
798
|
-
ret = getgrouplist(userInfo->pw_name, groupInfo->gr_gid,
|
885
|
+
int ret = getgrouplist(userInfo->pw_name, groupInfo->gr_gid,
|
799
886
|
groups, &info.ngroups);
|
800
887
|
if (ret == -1) {
|
801
888
|
int e = errno;
|
@@ -847,13 +934,13 @@ protected:
|
|
847
934
|
if (!options.baseURI.empty() && options.baseURI != "/") {
|
848
935
|
appendNullTerminatedKeyValue(result,
|
849
936
|
"RAILS_RELATIVE_URL_ROOT",
|
850
|
-
options.
|
937
|
+
options.baseURI);
|
851
938
|
appendNullTerminatedKeyValue(result,
|
852
939
|
"RACK_BASE_URI",
|
853
|
-
options.
|
940
|
+
options.baseURI);
|
854
941
|
appendNullTerminatedKeyValue(result,
|
855
942
|
"PASSENGER_BASE_URI",
|
856
|
-
options.
|
943
|
+
options.baseURI);
|
857
944
|
}
|
858
945
|
|
859
946
|
it = options.environmentVariables.begin();
|
@@ -1001,12 +1088,15 @@ protected:
|
|
1001
1088
|
}
|
1002
1089
|
}
|
1003
1090
|
|
1091
|
+
/**
|
1092
|
+
* Execute the process spawning negotiation protocol.
|
1093
|
+
*/
|
1004
1094
|
ProcessPtr negotiateSpawn(NegotiationDetails &details) {
|
1005
1095
|
TRACE_POINT();
|
1006
1096
|
details.spawnStartTime = SystemTime::getUsec();
|
1007
1097
|
details.gupid = integerToHex(SystemTime::get() / 60) + "-" +
|
1008
|
-
randomGenerator->generateAsciiString(11);
|
1009
|
-
details.connectPassword = randomGenerator->generateAsciiString(43);
|
1098
|
+
config->randomGenerator->generateAsciiString(11);
|
1099
|
+
details.connectPassword = config->randomGenerator->generateAsciiString(43);
|
1010
1100
|
details.timeout = details.options->startTimeout * 1000;
|
1011
1101
|
|
1012
1102
|
string result;
|
@@ -1025,6 +1115,7 @@ protected:
|
|
1025
1115
|
details);
|
1026
1116
|
}
|
1027
1117
|
|
1118
|
+
protocol_begin:
|
1028
1119
|
if (result == "I have control 1.0\n") {
|
1029
1120
|
UPDATE_TRACE_POINT();
|
1030
1121
|
sendSpawnRequest(details);
|
@@ -1046,6 +1137,8 @@ protected:
|
|
1046
1137
|
return handleSpawnResponse(details);
|
1047
1138
|
} else if (result == "Error\n") {
|
1048
1139
|
handleSpawnErrorResponse(details);
|
1140
|
+
} else if (result == "I have control 1.0\n") {
|
1141
|
+
goto protocol_begin;
|
1049
1142
|
} else {
|
1050
1143
|
handleInvalidSpawnResponseType(result, details);
|
1051
1144
|
}
|
@@ -1161,1107 +1254,6 @@ public:
|
|
1161
1254
|
typedef shared_ptr<Spawner> SpawnerPtr;
|
1162
1255
|
|
1163
1256
|
|
1164
|
-
class SmartSpawner: public Spawner, public enable_shared_from_this<SmartSpawner> {
|
1165
|
-
private:
|
1166
|
-
struct SpawnResult {
|
1167
|
-
pid_t pid;
|
1168
|
-
FileDescriptor adminSocket;
|
1169
|
-
BufferedIO io;
|
1170
|
-
};
|
1171
|
-
|
1172
|
-
/** The event loop that created Process objects should use, and that I/O forwarding
|
1173
|
-
* functions should use. For example data on the error pipe is forwarded using this event loop.
|
1174
|
-
*/
|
1175
|
-
SafeLibevPtr libev;
|
1176
|
-
const vector<string> preloaderCommand;
|
1177
|
-
map<string, string> preloaderAnnotations;
|
1178
|
-
Options options;
|
1179
|
-
ev::io preloaderOutputWatcher;
|
1180
|
-
shared_ptr<PipeWatcher> preloaderErrorWatcher;
|
1181
|
-
|
1182
|
-
// Protects m_lastUsed and pid.
|
1183
|
-
mutable boost::mutex simpleFieldSyncher;
|
1184
|
-
// Protects everything else.
|
1185
|
-
mutable boost::mutex syncher;
|
1186
|
-
|
1187
|
-
pid_t pid;
|
1188
|
-
FileDescriptor adminSocket;
|
1189
|
-
string socketAddress;
|
1190
|
-
unsigned long long m_lastUsed;
|
1191
|
-
|
1192
|
-
void onPreloaderOutputReadable(ev::io &io, int revents) {
|
1193
|
-
char buf[1024 * 8];
|
1194
|
-
ssize_t ret;
|
1195
|
-
|
1196
|
-
ret = syscalls::read(adminSocket, buf, sizeof(buf));
|
1197
|
-
if (ret <= 0) {
|
1198
|
-
preloaderOutputWatcher.stop();
|
1199
|
-
} else if (config->forwardStdout) {
|
1200
|
-
write(config->forwardStdoutTo, buf, ret);
|
1201
|
-
}
|
1202
|
-
}
|
1203
|
-
|
1204
|
-
string getPreloaderCommandString() const {
|
1205
|
-
string result;
|
1206
|
-
unsigned int i;
|
1207
|
-
|
1208
|
-
for (i = 0; i < preloaderCommand.size(); i++) {
|
1209
|
-
if (i != 0) {
|
1210
|
-
result.append(1, '\0');
|
1211
|
-
}
|
1212
|
-
result.append(preloaderCommand[i]);
|
1213
|
-
}
|
1214
|
-
return result;
|
1215
|
-
}
|
1216
|
-
|
1217
|
-
vector<string> createRealPreloaderCommand(const Options &options,
|
1218
|
-
shared_array<const char *> &args)
|
1219
|
-
{
|
1220
|
-
string agentsDir = resourceLocator.getAgentsDir();
|
1221
|
-
vector<string> command;
|
1222
|
-
|
1223
|
-
if (options.loadShellEnvvars) {
|
1224
|
-
command.push_back("bash");
|
1225
|
-
command.push_back("bash");
|
1226
|
-
command.push_back("-lc");
|
1227
|
-
command.push_back("exec \"$@\"");
|
1228
|
-
command.push_back("SpawnPreparerShell");
|
1229
|
-
} else {
|
1230
|
-
command.push_back(agentsDir + "/SpawnPreparer");
|
1231
|
-
}
|
1232
|
-
command.push_back(agentsDir + "/SpawnPreparer");
|
1233
|
-
command.push_back(serializeEnvvarsFromPoolOptions(options));
|
1234
|
-
command.push_back(preloaderCommand[0]);
|
1235
|
-
command.push_back("Passenger AppPreloader: " + options.appRoot);
|
1236
|
-
for (unsigned int i = 1; i < preloaderCommand.size(); i++) {
|
1237
|
-
command.push_back(preloaderCommand[i]);
|
1238
|
-
}
|
1239
|
-
|
1240
|
-
createCommandArgs(command, args);
|
1241
|
-
return command;
|
1242
|
-
}
|
1243
|
-
|
1244
|
-
void throwPreloaderSpawnException(const string &msg,
|
1245
|
-
SpawnException::ErrorKind errorKind,
|
1246
|
-
StartupDetails &details)
|
1247
|
-
{
|
1248
|
-
throwPreloaderSpawnException(msg, errorKind, details.stderrCapturer,
|
1249
|
-
details.debugDir);
|
1250
|
-
}
|
1251
|
-
|
1252
|
-
void throwPreloaderSpawnException(const string &msg,
|
1253
|
-
SpawnException::ErrorKind errorKind,
|
1254
|
-
BackgroundIOCapturerPtr &stderrCapturer,
|
1255
|
-
const DebugDirPtr &debugDir)
|
1256
|
-
{
|
1257
|
-
TRACE_POINT();
|
1258
|
-
// Stop the stderr capturing thread and get the captured stderr
|
1259
|
-
// output so far.
|
1260
|
-
string stderrOutput;
|
1261
|
-
if (stderrCapturer != NULL) {
|
1262
|
-
stderrOutput = stderrCapturer->stop();
|
1263
|
-
}
|
1264
|
-
|
1265
|
-
// If the exception wasn't due to a timeout, try to capture the
|
1266
|
-
// remaining stderr output for at most 2 seconds.
|
1267
|
-
if (errorKind != SpawnException::PRELOADER_STARTUP_TIMEOUT
|
1268
|
-
&& errorKind != SpawnException::APP_STARTUP_TIMEOUT
|
1269
|
-
&& stderrCapturer != NULL) {
|
1270
|
-
bool done = false;
|
1271
|
-
unsigned long long timeout = 2000;
|
1272
|
-
while (!done) {
|
1273
|
-
char buf[1024 * 32];
|
1274
|
-
unsigned int ret;
|
1275
|
-
|
1276
|
-
try {
|
1277
|
-
ret = readExact(stderrCapturer->getFd(), buf,
|
1278
|
-
sizeof(buf), &timeout);
|
1279
|
-
if (ret == 0) {
|
1280
|
-
done = true;
|
1281
|
-
} else {
|
1282
|
-
stderrOutput.append(buf, ret);
|
1283
|
-
}
|
1284
|
-
} catch (const SystemException &e) {
|
1285
|
-
P_WARN("Stderr I/O capture error: " << e.what());
|
1286
|
-
done = true;
|
1287
|
-
} catch (const TimeoutException &) {
|
1288
|
-
done = true;
|
1289
|
-
}
|
1290
|
-
}
|
1291
|
-
}
|
1292
|
-
stderrCapturer.reset();
|
1293
|
-
|
1294
|
-
// Now throw SpawnException with the captured stderr output
|
1295
|
-
// as error response.
|
1296
|
-
SpawnException e(msg, stderrOutput, false, errorKind);
|
1297
|
-
e.setPreloaderCommand(getPreloaderCommandString());
|
1298
|
-
annotatePreloaderException(e, debugDir);
|
1299
|
-
throw e;
|
1300
|
-
}
|
1301
|
-
|
1302
|
-
void annotatePreloaderException(SpawnException &e, const DebugDirPtr &debugDir) {
|
1303
|
-
if (debugDir != NULL) {
|
1304
|
-
e.addAnnotations(debugDir->readAll());
|
1305
|
-
}
|
1306
|
-
}
|
1307
|
-
|
1308
|
-
bool preloaderStarted() const {
|
1309
|
-
return pid != -1;
|
1310
|
-
}
|
1311
|
-
|
1312
|
-
void startPreloader() {
|
1313
|
-
TRACE_POINT();
|
1314
|
-
this_thread::disable_interruption di;
|
1315
|
-
this_thread::disable_syscall_interruption dsi;
|
1316
|
-
assert(!preloaderStarted());
|
1317
|
-
checkChrootDirectories(options);
|
1318
|
-
|
1319
|
-
shared_array<const char *> args;
|
1320
|
-
vector<string> command = createRealPreloaderCommand(options, args);
|
1321
|
-
SpawnPreparationInfo preparation = prepareSpawn(options);
|
1322
|
-
SocketPair adminSocket = createUnixSocketPair();
|
1323
|
-
Pipe errorPipe = createPipe();
|
1324
|
-
DebugDirPtr debugDir = make_shared<DebugDir>(preparation.uid, preparation.gid);
|
1325
|
-
pid_t pid;
|
1326
|
-
|
1327
|
-
pid = syscalls::fork();
|
1328
|
-
if (pid == 0) {
|
1329
|
-
setenv("PASSENGER_DEBUG_DIR", debugDir->getPath().c_str(), 1);
|
1330
|
-
purgeStdio(stdout);
|
1331
|
-
purgeStdio(stderr);
|
1332
|
-
resetSignalHandlersAndMask();
|
1333
|
-
disableMallocDebugging();
|
1334
|
-
int adminSocketCopy = dup2(adminSocket.first, 3);
|
1335
|
-
int errorPipeCopy = dup2(errorPipe.second, 4);
|
1336
|
-
dup2(adminSocketCopy, 0);
|
1337
|
-
dup2(adminSocketCopy, 1);
|
1338
|
-
dup2(errorPipeCopy, 2);
|
1339
|
-
closeAllFileDescriptors(2);
|
1340
|
-
setChroot(preparation);
|
1341
|
-
switchUser(preparation);
|
1342
|
-
setWorkingDirectory(preparation);
|
1343
|
-
execvp(command[0].c_str(), (char * const *) args.get());
|
1344
|
-
|
1345
|
-
int e = errno;
|
1346
|
-
printf("!> Error\n");
|
1347
|
-
printf("!> \n");
|
1348
|
-
printf("Cannot execute \"%s\": %s (errno=%d)\n", command[0].c_str(),
|
1349
|
-
strerror(e), e);
|
1350
|
-
fprintf(stderr, "Cannot execute \"%s\": %s (errno=%d)\n",
|
1351
|
-
command[0].c_str(), strerror(e), e);
|
1352
|
-
fflush(stdout);
|
1353
|
-
fflush(stderr);
|
1354
|
-
_exit(1);
|
1355
|
-
|
1356
|
-
} else if (pid == -1) {
|
1357
|
-
int e = errno;
|
1358
|
-
throw SystemException("Cannot fork a new process", e);
|
1359
|
-
|
1360
|
-
} else {
|
1361
|
-
ScopeGuard guard(boost::bind(nonInterruptableKillAndWaitpid, pid));
|
1362
|
-
adminSocket.first.close();
|
1363
|
-
errorPipe.second.close();
|
1364
|
-
|
1365
|
-
StartupDetails details;
|
1366
|
-
details.adminSocket = adminSocket.second;
|
1367
|
-
details.io = BufferedIO(adminSocket.second);
|
1368
|
-
details.stderrCapturer =
|
1369
|
-
make_shared<BackgroundIOCapturer>(
|
1370
|
-
errorPipe.first,
|
1371
|
-
config->forwardStderr ? config->forwardStderrTo : -1);
|
1372
|
-
details.stderrCapturer->start();
|
1373
|
-
details.debugDir = debugDir;
|
1374
|
-
details.options = &options;
|
1375
|
-
details.timeout = options.startTimeout * 1000;
|
1376
|
-
details.forwardStderr = config->forwardStderr;
|
1377
|
-
details.forwardStderrTo = config->forwardStderrTo;
|
1378
|
-
|
1379
|
-
{
|
1380
|
-
this_thread::restore_interruption ri(di);
|
1381
|
-
this_thread::restore_syscall_interruption rsi(dsi);
|
1382
|
-
socketAddress = negotiatePreloaderStartup(details);
|
1383
|
-
}
|
1384
|
-
this->adminSocket = adminSocket.second;
|
1385
|
-
{
|
1386
|
-
lock_guard<boost::mutex> l(simpleFieldSyncher);
|
1387
|
-
this->pid = pid;
|
1388
|
-
}
|
1389
|
-
preloaderOutputWatcher.set(adminSocket.second, ev::READ);
|
1390
|
-
libev->start(preloaderOutputWatcher);
|
1391
|
-
setNonBlocking(errorPipe.first);
|
1392
|
-
preloaderErrorWatcher = make_shared<PipeWatcher>(libev,
|
1393
|
-
errorPipe.first,
|
1394
|
-
config->forwardStderr ? config->forwardStderrTo : -1);
|
1395
|
-
preloaderErrorWatcher->start();
|
1396
|
-
preloaderAnnotations = debugDir->readAll();
|
1397
|
-
P_DEBUG("Preloader for " << options.appRoot <<
|
1398
|
-
" started on PID " << pid <<
|
1399
|
-
", listening on " << socketAddress);
|
1400
|
-
guard.clear();
|
1401
|
-
}
|
1402
|
-
}
|
1403
|
-
|
1404
|
-
void stopPreloader() {
|
1405
|
-
TRACE_POINT();
|
1406
|
-
this_thread::disable_interruption di;
|
1407
|
-
this_thread::disable_syscall_interruption dsi;
|
1408
|
-
|
1409
|
-
if (!preloaderStarted()) {
|
1410
|
-
return;
|
1411
|
-
}
|
1412
|
-
adminSocket.close();
|
1413
|
-
if (timedWaitpid(pid, NULL, 5000) == 0) {
|
1414
|
-
P_TRACE(2, "Spawn server did not exit in time, killing it...");
|
1415
|
-
syscalls::kill(pid, SIGKILL);
|
1416
|
-
syscalls::waitpid(pid, NULL, 0);
|
1417
|
-
}
|
1418
|
-
libev->stop(preloaderOutputWatcher);
|
1419
|
-
// Detach the error pipe; it will truly be closed after the error
|
1420
|
-
// pipe has reached EOF.
|
1421
|
-
preloaderErrorWatcher.reset();
|
1422
|
-
// Delete socket after the process has exited so that it
|
1423
|
-
// doesn't crash upon deleting a nonexistant file.
|
1424
|
-
// TODO: in Passenger 4 we must check whether the file really was
|
1425
|
-
// owned by the preloader, otherwise this is a potential security flaw.
|
1426
|
-
if (getSocketAddressType(socketAddress) == SAT_UNIX) {
|
1427
|
-
string filename = parseUnixSocketAddress(socketAddress);
|
1428
|
-
syscalls::unlink(filename.c_str());
|
1429
|
-
}
|
1430
|
-
{
|
1431
|
-
lock_guard<boost::mutex> l(simpleFieldSyncher);
|
1432
|
-
pid = -1;
|
1433
|
-
}
|
1434
|
-
socketAddress.clear();
|
1435
|
-
}
|
1436
|
-
|
1437
|
-
void sendStartupRequest(StartupDetails &details) {
|
1438
|
-
TRACE_POINT();
|
1439
|
-
try {
|
1440
|
-
writeExact(details.adminSocket,
|
1441
|
-
"You have control 1.0\n"
|
1442
|
-
"passenger_root: " + resourceLocator.getRoot() + "\n"
|
1443
|
-
"ruby_libdir: " + resourceLocator.getRubyLibDir() + "\n"
|
1444
|
-
"passenger_version: " PASSENGER_VERSION "\n"
|
1445
|
-
"generation_dir: " + generation->getPath() + "\n",
|
1446
|
-
&details.timeout);
|
1447
|
-
|
1448
|
-
vector<string> args;
|
1449
|
-
vector<string>::const_iterator it, end;
|
1450
|
-
details.options->toVector(args, resourceLocator);
|
1451
|
-
for (it = args.begin(); it != args.end(); it++) {
|
1452
|
-
const string &key = *it;
|
1453
|
-
it++;
|
1454
|
-
const string &value = *it;
|
1455
|
-
writeExact(details.adminSocket,
|
1456
|
-
key + ": " + value + "\n",
|
1457
|
-
&details.timeout);
|
1458
|
-
}
|
1459
|
-
writeExact(details.adminSocket, "\n", &details.timeout);
|
1460
|
-
} catch (const SystemException &e) {
|
1461
|
-
if (e.code() == EPIPE) {
|
1462
|
-
/* Ignore this. Process might have written an
|
1463
|
-
* error response before reading the arguments,
|
1464
|
-
* in which case we'll want to show that instead.
|
1465
|
-
*/
|
1466
|
-
} else {
|
1467
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1468
|
-
"the preloader. There was an I/O error while "
|
1469
|
-
"sending the startup request message to it: " +
|
1470
|
-
e.sys(),
|
1471
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1472
|
-
details);
|
1473
|
-
}
|
1474
|
-
} catch (const TimeoutException &) {
|
1475
|
-
throwPreloaderSpawnException("An error occurred while starting up the "
|
1476
|
-
"preloader: it did not read the startup request message in time.",
|
1477
|
-
SpawnException::PRELOADER_STARTUP_TIMEOUT,
|
1478
|
-
details);
|
1479
|
-
}
|
1480
|
-
}
|
1481
|
-
|
1482
|
-
string handleStartupResponse(StartupDetails &details) {
|
1483
|
-
TRACE_POINT();
|
1484
|
-
string socketAddress;
|
1485
|
-
|
1486
|
-
while (true) {
|
1487
|
-
string line;
|
1488
|
-
|
1489
|
-
try {
|
1490
|
-
line = readMessageLine(details);
|
1491
|
-
} catch (const SystemException &e) {
|
1492
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1493
|
-
"the preloader. There was an I/O error while reading its "
|
1494
|
-
"startup response: " + e.sys(),
|
1495
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1496
|
-
details);
|
1497
|
-
} catch (const TimeoutException &) {
|
1498
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1499
|
-
"the preloader: it did not write a startup response in time.",
|
1500
|
-
SpawnException::PRELOADER_STARTUP_TIMEOUT,
|
1501
|
-
details);
|
1502
|
-
}
|
1503
|
-
|
1504
|
-
if (line.empty()) {
|
1505
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1506
|
-
"the preloader. It unexpected closed the connection while "
|
1507
|
-
"sending its startup response.",
|
1508
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1509
|
-
details);
|
1510
|
-
} else if (line[line.size() - 1] != '\n') {
|
1511
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1512
|
-
"the preloader. It sent a line without a newline character "
|
1513
|
-
"in its startup response.",
|
1514
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1515
|
-
details);
|
1516
|
-
} else if (line == "\n") {
|
1517
|
-
break;
|
1518
|
-
}
|
1519
|
-
|
1520
|
-
string::size_type pos = line.find(": ");
|
1521
|
-
if (pos == string::npos) {
|
1522
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1523
|
-
"the preloader. It sent a startup response line without "
|
1524
|
-
"separator.",
|
1525
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1526
|
-
details);
|
1527
|
-
}
|
1528
|
-
|
1529
|
-
string key = line.substr(0, pos);
|
1530
|
-
string value = line.substr(pos + 2, line.size() - pos - 3);
|
1531
|
-
if (key == "socket") {
|
1532
|
-
socketAddress = fixupSocketAddress(options, value);
|
1533
|
-
} else {
|
1534
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1535
|
-
"the preloader. It sent an unknown startup response line "
|
1536
|
-
"called '" + key + "'.",
|
1537
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1538
|
-
details);
|
1539
|
-
}
|
1540
|
-
}
|
1541
|
-
|
1542
|
-
if (socketAddress.empty()) {
|
1543
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1544
|
-
"the preloader. It did not report a socket address in its "
|
1545
|
-
"startup response.",
|
1546
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1547
|
-
details);
|
1548
|
-
}
|
1549
|
-
|
1550
|
-
return socketAddress;
|
1551
|
-
}
|
1552
|
-
|
1553
|
-
void handleErrorResponse(StartupDetails &details) {
|
1554
|
-
TRACE_POINT();
|
1555
|
-
map<string, string> attributes;
|
1556
|
-
|
1557
|
-
while (true) {
|
1558
|
-
string line;
|
1559
|
-
|
1560
|
-
try {
|
1561
|
-
line = readMessageLine(details);
|
1562
|
-
} catch (const SystemException &e) {
|
1563
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1564
|
-
"the preloader. There was an I/O error while reading its "
|
1565
|
-
"startup response: " + e.sys(),
|
1566
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1567
|
-
details);
|
1568
|
-
} catch (const TimeoutException &) {
|
1569
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1570
|
-
"the preloader: it did not write a startup response in time.",
|
1571
|
-
SpawnException::PRELOADER_STARTUP_TIMEOUT,
|
1572
|
-
details);
|
1573
|
-
}
|
1574
|
-
|
1575
|
-
if (line.empty()) {
|
1576
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1577
|
-
"the preloader. It unexpected closed the connection while "
|
1578
|
-
"sending its startup response.",
|
1579
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1580
|
-
details);
|
1581
|
-
} else if (line[line.size() - 1] != '\n') {
|
1582
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1583
|
-
"the preloader. It sent a line without a newline character "
|
1584
|
-
"in its startup response.",
|
1585
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1586
|
-
details);
|
1587
|
-
} else if (line == "\n") {
|
1588
|
-
break;
|
1589
|
-
}
|
1590
|
-
|
1591
|
-
string::size_type pos = line.find(": ");
|
1592
|
-
if (pos == string::npos) {
|
1593
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1594
|
-
"the preloader. It sent a startup response line without "
|
1595
|
-
"separator.",
|
1596
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1597
|
-
details);
|
1598
|
-
}
|
1599
|
-
|
1600
|
-
string key = line.substr(0, pos);
|
1601
|
-
string value = line.substr(pos + 2, line.size() - pos - 3);
|
1602
|
-
attributes[key] = value;
|
1603
|
-
}
|
1604
|
-
|
1605
|
-
try {
|
1606
|
-
string message = details.io.readAll(&details.timeout);
|
1607
|
-
SpawnException e("An error occured while starting up the preloader.",
|
1608
|
-
message,
|
1609
|
-
attributes["html"] == "true",
|
1610
|
-
SpawnException::PRELOADER_STARTUP_EXPLAINABLE_ERROR);
|
1611
|
-
e.setPreloaderCommand(getPreloaderCommandString());
|
1612
|
-
annotatePreloaderException(e, details.debugDir);
|
1613
|
-
throw e;
|
1614
|
-
} catch (const SystemException &e) {
|
1615
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1616
|
-
"the preloader. It tried to report an error message, but "
|
1617
|
-
"an I/O error occurred while reading this error message: " +
|
1618
|
-
e.sys(),
|
1619
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1620
|
-
details);
|
1621
|
-
} catch (const TimeoutException &) {
|
1622
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1623
|
-
"the preloader. It tried to report an error message, but "
|
1624
|
-
"it took too much time doing that.",
|
1625
|
-
SpawnException::PRELOADER_STARTUP_TIMEOUT,
|
1626
|
-
details);
|
1627
|
-
}
|
1628
|
-
}
|
1629
|
-
|
1630
|
-
void handleInvalidResponseType(StartupDetails &details, const string &line) {
|
1631
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1632
|
-
"the preloader. It sent an unknown response type \"" +
|
1633
|
-
cEscapeString(line) + "\".",
|
1634
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1635
|
-
details);
|
1636
|
-
}
|
1637
|
-
|
1638
|
-
string negotiatePreloaderStartup(StartupDetails &details) {
|
1639
|
-
TRACE_POINT();
|
1640
|
-
string result;
|
1641
|
-
try {
|
1642
|
-
result = readMessageLine(details);
|
1643
|
-
} catch (const SystemException &e) {
|
1644
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1645
|
-
"the preloader. There was an I/O error while reading its "
|
1646
|
-
"handshake message: " + e.sys(),
|
1647
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1648
|
-
details);
|
1649
|
-
} catch (const TimeoutException &) {
|
1650
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1651
|
-
"the preloader: it did not write a handshake message in time.",
|
1652
|
-
SpawnException::PRELOADER_STARTUP_TIMEOUT,
|
1653
|
-
details);
|
1654
|
-
}
|
1655
|
-
|
1656
|
-
if (result == "I have control 1.0\n") {
|
1657
|
-
UPDATE_TRACE_POINT();
|
1658
|
-
sendStartupRequest(details);
|
1659
|
-
try {
|
1660
|
-
result = readMessageLine(details);
|
1661
|
-
} catch (const SystemException &e) {
|
1662
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1663
|
-
"the preloader. There was an I/O error while reading its "
|
1664
|
-
"startup response: " + e.sys(),
|
1665
|
-
SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
|
1666
|
-
details);
|
1667
|
-
} catch (const TimeoutException &) {
|
1668
|
-
throwPreloaderSpawnException("An error occurred while starting up "
|
1669
|
-
"the preloader: it did not write a startup response in time.",
|
1670
|
-
SpawnException::PRELOADER_STARTUP_TIMEOUT,
|
1671
|
-
details);
|
1672
|
-
}
|
1673
|
-
if (result == "Ready\n") {
|
1674
|
-
return handleStartupResponse(details);
|
1675
|
-
} else if (result == "Error\n") {
|
1676
|
-
handleErrorResponse(details);
|
1677
|
-
} else {
|
1678
|
-
handleInvalidResponseType(details, result);
|
1679
|
-
}
|
1680
|
-
} else {
|
1681
|
-
UPDATE_TRACE_POINT();
|
1682
|
-
if (result == "Error\n") {
|
1683
|
-
handleErrorResponse(details);
|
1684
|
-
} else {
|
1685
|
-
handleInvalidResponseType(details, result);
|
1686
|
-
}
|
1687
|
-
}
|
1688
|
-
|
1689
|
-
// Never reached, shut up compiler warning.
|
1690
|
-
abort();
|
1691
|
-
return "";
|
1692
|
-
}
|
1693
|
-
|
1694
|
-
SpawnResult sendSpawnCommand(const Options &options) {
|
1695
|
-
TRACE_POINT();
|
1696
|
-
FileDescriptor fd;
|
1697
|
-
try {
|
1698
|
-
fd = connectToServer(socketAddress);
|
1699
|
-
} catch (const SystemException &e) {
|
1700
|
-
BackgroundIOCapturerPtr stderrCapturer;
|
1701
|
-
throwPreloaderSpawnException("An error occurred while starting "
|
1702
|
-
"the application. Unable to connect to the preloader's "
|
1703
|
-
"socket: " + string(e.what()),
|
1704
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1705
|
-
stderrCapturer,
|
1706
|
-
DebugDirPtr());
|
1707
|
-
}
|
1708
|
-
|
1709
|
-
UPDATE_TRACE_POINT();
|
1710
|
-
BufferedIO io(fd);
|
1711
|
-
unsigned long long timeout = options.startTimeout * 1000;
|
1712
|
-
string result;
|
1713
|
-
vector<string> args;
|
1714
|
-
vector<string>::const_iterator it;
|
1715
|
-
|
1716
|
-
writeExact(fd, "spawn\n", &timeout);
|
1717
|
-
options.toVector(args, resourceLocator);
|
1718
|
-
for (it = args.begin(); it != args.end(); it++) {
|
1719
|
-
const string &key = *it;
|
1720
|
-
it++;
|
1721
|
-
const string &value = *it;
|
1722
|
-
writeExact(fd, key + ": " + value + "\n", &timeout);
|
1723
|
-
}
|
1724
|
-
writeExact(fd, "\n", &timeout);
|
1725
|
-
|
1726
|
-
result = io.readLine(1024, &timeout);
|
1727
|
-
if (result == "OK\n") {
|
1728
|
-
UPDATE_TRACE_POINT();
|
1729
|
-
pid_t spawnedPid;
|
1730
|
-
|
1731
|
-
spawnedPid = atoi(io.readLine(1024, &timeout).c_str());
|
1732
|
-
if (spawnedPid <= 0) {
|
1733
|
-
BackgroundIOCapturerPtr stderrCapturer;
|
1734
|
-
throwPreloaderSpawnException("An error occurred while starting "
|
1735
|
-
"the web application. Its preloader responded to the "
|
1736
|
-
"'spawn' command with an invalid PID: '" +
|
1737
|
-
toString(spawnedPid) + "'",
|
1738
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1739
|
-
stderrCapturer,
|
1740
|
-
DebugDirPtr());
|
1741
|
-
}
|
1742
|
-
// TODO: we really should be checking UID.
|
1743
|
-
// FIXME: for Passenger 4 we *must* check the UID otherwise this is a gaping security hole.
|
1744
|
-
if (getsid(spawnedPid) != getsid(pid)) {
|
1745
|
-
BackgroundIOCapturerPtr stderrCapturer;
|
1746
|
-
throwPreloaderSpawnException("An error occurred while starting "
|
1747
|
-
"the web application. Its preloader responded to the "
|
1748
|
-
"'spawn' command with a PID that doesn't belong to "
|
1749
|
-
"the same session: '" + toString(spawnedPid) + "'",
|
1750
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1751
|
-
stderrCapturer,
|
1752
|
-
DebugDirPtr());
|
1753
|
-
}
|
1754
|
-
|
1755
|
-
SpawnResult result;
|
1756
|
-
result.pid = spawnedPid;
|
1757
|
-
result.adminSocket = fd;
|
1758
|
-
result.io = io;
|
1759
|
-
return result;
|
1760
|
-
|
1761
|
-
} else if (result == "Error\n") {
|
1762
|
-
UPDATE_TRACE_POINT();
|
1763
|
-
NegotiationDetails details;
|
1764
|
-
details.io = io;
|
1765
|
-
details.timeout = timeout;
|
1766
|
-
handleSpawnErrorResponse(details);
|
1767
|
-
|
1768
|
-
} else {
|
1769
|
-
UPDATE_TRACE_POINT();
|
1770
|
-
NegotiationDetails details;
|
1771
|
-
handleInvalidSpawnResponseType(result, details);
|
1772
|
-
}
|
1773
|
-
|
1774
|
-
return SpawnResult(); // Never reached.
|
1775
|
-
}
|
1776
|
-
|
1777
|
-
template<typename Exception>
|
1778
|
-
SpawnResult sendSpawnCommandAgain(const Exception &e, const Options &options) {
|
1779
|
-
TRACE_POINT();
|
1780
|
-
P_WARN("An error occurred while spawning a process: " << e.what());
|
1781
|
-
P_WARN("The application preloader seems to have crashed, restarting it and trying again...");
|
1782
|
-
stopPreloader();
|
1783
|
-
startPreloader();
|
1784
|
-
ScopeGuard guard(boost::bind(&SmartSpawner::stopPreloader, this));
|
1785
|
-
SpawnResult result = sendSpawnCommand(options);
|
1786
|
-
guard.clear();
|
1787
|
-
return result;
|
1788
|
-
}
|
1789
|
-
|
1790
|
-
protected:
|
1791
|
-
virtual void annotateAppSpawnException(SpawnException &e, NegotiationDetails &details) {
|
1792
|
-
Spawner::annotateAppSpawnException(e, details);
|
1793
|
-
e.addAnnotations(preloaderAnnotations);
|
1794
|
-
}
|
1795
|
-
|
1796
|
-
public:
|
1797
|
-
SmartSpawner(const SafeLibevPtr &_libev,
|
1798
|
-
const ResourceLocator &_resourceLocator,
|
1799
|
-
const ServerInstanceDir::GenerationPtr &_generation,
|
1800
|
-
const vector<string> &_preloaderCommand,
|
1801
|
-
const Options &_options,
|
1802
|
-
const RandomGeneratorPtr &_randomGenerator = RandomGeneratorPtr(),
|
1803
|
-
const SpawnerConfigPtr &_config = SpawnerConfigPtr())
|
1804
|
-
: Spawner(_resourceLocator),
|
1805
|
-
libev(_libev),
|
1806
|
-
preloaderCommand(_preloaderCommand)
|
1807
|
-
{
|
1808
|
-
if (preloaderCommand.size() < 2) {
|
1809
|
-
throw ArgumentException("preloaderCommand must have at least 2 elements");
|
1810
|
-
}
|
1811
|
-
|
1812
|
-
generation = _generation;
|
1813
|
-
options = _options.copyAndPersist().clearLogger();
|
1814
|
-
pid = -1;
|
1815
|
-
m_lastUsed = SystemTime::getUsec();
|
1816
|
-
|
1817
|
-
preloaderOutputWatcher.set<SmartSpawner, &SmartSpawner::onPreloaderOutputReadable>(this);
|
1818
|
-
|
1819
|
-
if (_randomGenerator == NULL) {
|
1820
|
-
randomGenerator = make_shared<RandomGenerator>();
|
1821
|
-
} else {
|
1822
|
-
randomGenerator = _randomGenerator;
|
1823
|
-
}
|
1824
|
-
if (_config == NULL) {
|
1825
|
-
config = make_shared<SpawnerConfig>();
|
1826
|
-
} else {
|
1827
|
-
config = _config;
|
1828
|
-
}
|
1829
|
-
}
|
1830
|
-
|
1831
|
-
virtual ~SmartSpawner() {
|
1832
|
-
lock_guard<boost::mutex> l(syncher);
|
1833
|
-
stopPreloader();
|
1834
|
-
}
|
1835
|
-
|
1836
|
-
virtual ProcessPtr spawn(const Options &options) {
|
1837
|
-
TRACE_POINT();
|
1838
|
-
assert(options.appType == this->options.appType);
|
1839
|
-
assert(options.appRoot == this->options.appRoot);
|
1840
|
-
|
1841
|
-
P_DEBUG("Spawning new process: appRoot=" << options.appRoot);
|
1842
|
-
possiblyRaiseInternalError(options);
|
1843
|
-
|
1844
|
-
{
|
1845
|
-
lock_guard<boost::mutex> l(simpleFieldSyncher);
|
1846
|
-
m_lastUsed = SystemTime::getUsec();
|
1847
|
-
}
|
1848
|
-
if (!preloaderStarted()) {
|
1849
|
-
UPDATE_TRACE_POINT();
|
1850
|
-
startPreloader();
|
1851
|
-
}
|
1852
|
-
|
1853
|
-
UPDATE_TRACE_POINT();
|
1854
|
-
SpawnResult result;
|
1855
|
-
try {
|
1856
|
-
result = sendSpawnCommand(options);
|
1857
|
-
} catch (const SystemException &e) {
|
1858
|
-
result = sendSpawnCommandAgain(e, options);
|
1859
|
-
} catch (const IOException &e) {
|
1860
|
-
result = sendSpawnCommandAgain(e, options);
|
1861
|
-
} catch (const SpawnException &e) {
|
1862
|
-
result = sendSpawnCommandAgain(e, options);
|
1863
|
-
}
|
1864
|
-
|
1865
|
-
UPDATE_TRACE_POINT();
|
1866
|
-
NegotiationDetails details;
|
1867
|
-
details.libev = libev;
|
1868
|
-
details.pid = result.pid;
|
1869
|
-
details.adminSocket = result.adminSocket;
|
1870
|
-
details.io = result.io;
|
1871
|
-
details.options = &options;
|
1872
|
-
details.forwardStderr = config->forwardStderr;
|
1873
|
-
details.forwardStderrTo = config->forwardStderrTo;
|
1874
|
-
ProcessPtr process = negotiateSpawn(details);
|
1875
|
-
P_DEBUG("Process spawning done: appRoot=" << options.appRoot <<
|
1876
|
-
", pid=" << process->pid);
|
1877
|
-
return process;
|
1878
|
-
}
|
1879
|
-
|
1880
|
-
virtual bool cleanable() const {
|
1881
|
-
return true;
|
1882
|
-
}
|
1883
|
-
|
1884
|
-
virtual void cleanup() {
|
1885
|
-
TRACE_POINT();
|
1886
|
-
{
|
1887
|
-
lock_guard<boost::mutex> l(simpleFieldSyncher);
|
1888
|
-
m_lastUsed = SystemTime::getUsec();
|
1889
|
-
}
|
1890
|
-
lock_guard<boost::mutex> lock(syncher);
|
1891
|
-
stopPreloader();
|
1892
|
-
}
|
1893
|
-
|
1894
|
-
virtual unsigned long long lastUsed() const {
|
1895
|
-
lock_guard<boost::mutex> lock(simpleFieldSyncher);
|
1896
|
-
return m_lastUsed;
|
1897
|
-
}
|
1898
|
-
|
1899
|
-
pid_t getPreloaderPid() const {
|
1900
|
-
lock_guard<boost::mutex> lock(simpleFieldSyncher);
|
1901
|
-
return pid;
|
1902
|
-
}
|
1903
|
-
};
|
1904
|
-
|
1905
|
-
|
1906
|
-
class DirectSpawner: public Spawner {
|
1907
|
-
private:
|
1908
|
-
SafeLibevPtr libev;
|
1909
|
-
|
1910
|
-
static int startBackgroundThread(void *(*mainFunction)(void *), void *arg) {
|
1911
|
-
// Using raw pthread API because we don't want to register such
|
1912
|
-
// trivial threads on the oxt::thread list.
|
1913
|
-
pthread_t thr;
|
1914
|
-
pthread_attr_t attr;
|
1915
|
-
size_t stack_size = 96 * 1024;
|
1916
|
-
|
1917
|
-
unsigned long min_stack_size;
|
1918
|
-
bool stack_min_size_defined;
|
1919
|
-
bool round_stack_size;
|
1920
|
-
int ret;
|
1921
|
-
|
1922
|
-
#ifdef PTHREAD_STACK_MIN
|
1923
|
-
// PTHREAD_STACK_MIN may not be a constant macro so we need
|
1924
|
-
// to evaluate it dynamically.
|
1925
|
-
min_stack_size = PTHREAD_STACK_MIN;
|
1926
|
-
stack_min_size_defined = true;
|
1927
|
-
#else
|
1928
|
-
// Assume minimum stack size is 128 KB.
|
1929
|
-
min_stack_size = 128 * 1024;
|
1930
|
-
stack_min_size_defined = false;
|
1931
|
-
#endif
|
1932
|
-
if (stack_size != 0 && stack_size < min_stack_size) {
|
1933
|
-
stack_size = min_stack_size;
|
1934
|
-
round_stack_size = !stack_min_size_defined;
|
1935
|
-
} else {
|
1936
|
-
round_stack_size = true;
|
1937
|
-
}
|
1938
|
-
|
1939
|
-
if (round_stack_size) {
|
1940
|
-
// Round stack size up to page boundary.
|
1941
|
-
long page_size;
|
1942
|
-
#if defined(_SC_PAGESIZE)
|
1943
|
-
page_size = sysconf(_SC_PAGESIZE);
|
1944
|
-
#elif defined(_SC_PAGE_SIZE)
|
1945
|
-
page_size = sysconf(_SC_PAGE_SIZE);
|
1946
|
-
#elif defined(PAGESIZE)
|
1947
|
-
page_size = sysconf(PAGESIZE);
|
1948
|
-
#elif defined(PAGE_SIZE)
|
1949
|
-
page_size = sysconf(PAGE_SIZE);
|
1950
|
-
#else
|
1951
|
-
page_size = getpagesize();
|
1952
|
-
#endif
|
1953
|
-
if (stack_size % page_size != 0) {
|
1954
|
-
stack_size = stack_size - (stack_size % page_size) + page_size;
|
1955
|
-
}
|
1956
|
-
}
|
1957
|
-
|
1958
|
-
pthread_attr_init(&attr);
|
1959
|
-
pthread_attr_setdetachstate(&attr, 1);
|
1960
|
-
pthread_attr_setstacksize(&attr, stack_size);
|
1961
|
-
ret = pthread_create(&thr, &attr, mainFunction, arg);
|
1962
|
-
pthread_attr_destroy(&attr);
|
1963
|
-
return ret;
|
1964
|
-
}
|
1965
|
-
|
1966
|
-
static void *detachProcessMain(void *arg) {
|
1967
|
-
this_thread::disable_syscall_interruption dsi;
|
1968
|
-
pid_t pid = (pid_t) (long) arg;
|
1969
|
-
syscalls::waitpid(pid, NULL, 0);
|
1970
|
-
return NULL;
|
1971
|
-
}
|
1972
|
-
|
1973
|
-
void detachProcess(pid_t pid) {
|
1974
|
-
startBackgroundThread(detachProcessMain, (void *) (long) pid);
|
1975
|
-
}
|
1976
|
-
|
1977
|
-
vector<string> createCommand(const Options &options, shared_array<const char *> &args) const {
|
1978
|
-
vector<string> startCommandArgs;
|
1979
|
-
string processTitle;
|
1980
|
-
string agentsDir = resourceLocator.getAgentsDir();
|
1981
|
-
vector<string> command;
|
1982
|
-
|
1983
|
-
split(options.getStartCommand(resourceLocator), '\1', startCommandArgs);
|
1984
|
-
if (startCommandArgs.empty()) {
|
1985
|
-
throw RuntimeException("No startCommand given");
|
1986
|
-
}
|
1987
|
-
if (options.getProcessTitle().empty()) {
|
1988
|
-
processTitle = startCommandArgs[0];
|
1989
|
-
} else {
|
1990
|
-
processTitle = options.getProcessTitle() + ": " + options.appRoot;
|
1991
|
-
}
|
1992
|
-
|
1993
|
-
if (options.loadShellEnvvars) {
|
1994
|
-
command.push_back("bash");
|
1995
|
-
command.push_back("bash");
|
1996
|
-
command.push_back("-lc");
|
1997
|
-
command.push_back("exec \"$@\"");
|
1998
|
-
command.push_back("SpawnPreparerShell");
|
1999
|
-
} else {
|
2000
|
-
command.push_back(agentsDir + "/SpawnPreparer");
|
2001
|
-
}
|
2002
|
-
command.push_back(agentsDir + "/SpawnPreparer");
|
2003
|
-
command.push_back(serializeEnvvarsFromPoolOptions(options));
|
2004
|
-
command.push_back(startCommandArgs[0]);
|
2005
|
-
command.push_back(processTitle);
|
2006
|
-
for (unsigned int i = 1; i < startCommandArgs.size(); i++) {
|
2007
|
-
command.push_back(startCommandArgs[i]);
|
2008
|
-
}
|
2009
|
-
|
2010
|
-
createCommandArgs(command, args);
|
2011
|
-
return command;
|
2012
|
-
}
|
2013
|
-
|
2014
|
-
public:
|
2015
|
-
DirectSpawner(const SafeLibevPtr &_libev,
|
2016
|
-
const ResourceLocator &_resourceLocator,
|
2017
|
-
const ServerInstanceDir::GenerationPtr &_generation,
|
2018
|
-
const RandomGeneratorPtr &_randomGenerator = RandomGeneratorPtr(),
|
2019
|
-
const SpawnerConfigPtr &_config = SpawnerConfigPtr())
|
2020
|
-
: Spawner(_resourceLocator),
|
2021
|
-
libev(_libev)
|
2022
|
-
{
|
2023
|
-
generation = _generation;
|
2024
|
-
if (_randomGenerator == NULL) {
|
2025
|
-
randomGenerator = make_shared<RandomGenerator>();
|
2026
|
-
} else {
|
2027
|
-
randomGenerator = _randomGenerator;
|
2028
|
-
}
|
2029
|
-
if (_config == NULL) {
|
2030
|
-
config = make_shared<SpawnerConfig>();
|
2031
|
-
} else {
|
2032
|
-
config = _config;
|
2033
|
-
}
|
2034
|
-
}
|
2035
|
-
|
2036
|
-
virtual ProcessPtr spawn(const Options &options) {
|
2037
|
-
TRACE_POINT();
|
2038
|
-
this_thread::disable_interruption di;
|
2039
|
-
this_thread::disable_syscall_interruption dsi;
|
2040
|
-
P_DEBUG("Spawning new process: appRoot=" << options.appRoot);
|
2041
|
-
possiblyRaiseInternalError(options);
|
2042
|
-
|
2043
|
-
shared_array<const char *> args;
|
2044
|
-
vector<string> command = createCommand(options, args);
|
2045
|
-
SpawnPreparationInfo preparation = prepareSpawn(options);
|
2046
|
-
SocketPair adminSocket = createUnixSocketPair();
|
2047
|
-
Pipe errorPipe = createPipe();
|
2048
|
-
DebugDirPtr debugDir = make_shared<DebugDir>(preparation.uid, preparation.gid);
|
2049
|
-
pid_t pid;
|
2050
|
-
|
2051
|
-
pid = syscalls::fork();
|
2052
|
-
if (pid == 0) {
|
2053
|
-
setenv("PASSENGER_DEBUG_DIR", debugDir->getPath().c_str(), 1);
|
2054
|
-
purgeStdio(stdout);
|
2055
|
-
purgeStdio(stderr);
|
2056
|
-
resetSignalHandlersAndMask();
|
2057
|
-
disableMallocDebugging();
|
2058
|
-
int adminSocketCopy = dup2(adminSocket.first, 3);
|
2059
|
-
int errorPipeCopy = dup2(errorPipe.second, 4);
|
2060
|
-
dup2(adminSocketCopy, 0);
|
2061
|
-
dup2(adminSocketCopy, 1);
|
2062
|
-
dup2(errorPipeCopy, 2);
|
2063
|
-
closeAllFileDescriptors(2);
|
2064
|
-
setChroot(preparation);
|
2065
|
-
switchUser(preparation);
|
2066
|
-
setWorkingDirectory(preparation);
|
2067
|
-
execvp(args[0], (char * const *) args.get());
|
2068
|
-
|
2069
|
-
int e = errno;
|
2070
|
-
printf("!> Error\n");
|
2071
|
-
printf("!> \n");
|
2072
|
-
printf("Cannot execute \"%s\": %s (errno=%d)\n", command[0].c_str(),
|
2073
|
-
strerror(e), e);
|
2074
|
-
fprintf(stderr, "Cannot execute \"%s\": %s (errno=%d)\n",
|
2075
|
-
command[0].c_str(), strerror(e), e);
|
2076
|
-
fflush(stdout);
|
2077
|
-
fflush(stderr);
|
2078
|
-
_exit(1);
|
2079
|
-
|
2080
|
-
} else if (pid == -1) {
|
2081
|
-
int e = errno;
|
2082
|
-
throw SystemException("Cannot fork a new process", e);
|
2083
|
-
|
2084
|
-
} else {
|
2085
|
-
UPDATE_TRACE_POINT();
|
2086
|
-
ScopeGuard guard(boost::bind(nonInterruptableKillAndWaitpid, pid));
|
2087
|
-
adminSocket.first.close();
|
2088
|
-
errorPipe.second.close();
|
2089
|
-
|
2090
|
-
NegotiationDetails details;
|
2091
|
-
details.libev = libev;
|
2092
|
-
details.stderrCapturer =
|
2093
|
-
make_shared<BackgroundIOCapturer>(
|
2094
|
-
errorPipe.first,
|
2095
|
-
config->forwardStderr ? config->forwardStderrTo : -1);
|
2096
|
-
details.stderrCapturer->start();
|
2097
|
-
details.pid = pid;
|
2098
|
-
details.adminSocket = adminSocket.second;
|
2099
|
-
details.io = BufferedIO(adminSocket.second);
|
2100
|
-
details.errorPipe = errorPipe.first;
|
2101
|
-
details.options = &options;
|
2102
|
-
details.forwardStderr = config->forwardStderr;
|
2103
|
-
details.forwardStderrTo = config->forwardStderrTo;
|
2104
|
-
details.debugDir = debugDir;
|
2105
|
-
|
2106
|
-
ProcessPtr process;
|
2107
|
-
{
|
2108
|
-
this_thread::restore_interruption ri(di);
|
2109
|
-
this_thread::restore_syscall_interruption rsi(dsi);
|
2110
|
-
process = negotiateSpawn(details);
|
2111
|
-
}
|
2112
|
-
detachProcess(process->pid);
|
2113
|
-
guard.clear();
|
2114
|
-
P_DEBUG("Process spawning done: appRoot=" << options.appRoot <<
|
2115
|
-
", pid=" << process->pid);
|
2116
|
-
return process;
|
2117
|
-
}
|
2118
|
-
}
|
2119
|
-
};
|
2120
|
-
|
2121
|
-
|
2122
|
-
class DummySpawner: public Spawner {
|
2123
|
-
private:
|
2124
|
-
SpawnerConfigPtr config;
|
2125
|
-
boost::mutex lock;
|
2126
|
-
unsigned int count;
|
2127
|
-
|
2128
|
-
public:
|
2129
|
-
unsigned int cleanCount;
|
2130
|
-
|
2131
|
-
DummySpawner(const ResourceLocator &resourceLocator, const SpawnerConfigPtr &_config)
|
2132
|
-
: Spawner(resourceLocator),
|
2133
|
-
config(_config)
|
2134
|
-
{
|
2135
|
-
count = 0;
|
2136
|
-
cleanCount = 0;
|
2137
|
-
}
|
2138
|
-
|
2139
|
-
virtual ProcessPtr spawn(const Options &options) {
|
2140
|
-
TRACE_POINT();
|
2141
|
-
possiblyRaiseInternalError(options);
|
2142
|
-
|
2143
|
-
SocketPair adminSocket = createUnixSocketPair();
|
2144
|
-
SocketListPtr sockets = make_shared<SocketList>();
|
2145
|
-
sockets->add("main", "tcp://127.0.0.1:1234", "session", config->concurrency);
|
2146
|
-
syscalls::usleep(config->spawnTime);
|
2147
|
-
|
2148
|
-
lock_guard<boost::mutex> l(lock);
|
2149
|
-
count++;
|
2150
|
-
return make_shared<Process>(SafeLibevPtr(),
|
2151
|
-
(pid_t) count, "gupid-" + toString(count),
|
2152
|
-
toString(count),
|
2153
|
-
adminSocket.second, FileDescriptor(), sockets,
|
2154
|
-
SystemTime::getUsec(), SystemTime::getUsec());
|
2155
|
-
}
|
2156
|
-
|
2157
|
-
virtual bool cleanable() const {
|
2158
|
-
return true;
|
2159
|
-
}
|
2160
|
-
|
2161
|
-
virtual void cleanup() {
|
2162
|
-
cleanCount++;
|
2163
|
-
}
|
2164
|
-
};
|
2165
|
-
|
2166
|
-
typedef shared_ptr<DummySpawner> DummySpawnerPtr;
|
2167
|
-
|
2168
|
-
|
2169
|
-
class SpawnerFactory {
|
2170
|
-
private:
|
2171
|
-
SafeLibevPtr libev;
|
2172
|
-
ResourceLocator resourceLocator;
|
2173
|
-
ServerInstanceDir::GenerationPtr generation;
|
2174
|
-
RandomGeneratorPtr randomGenerator;
|
2175
|
-
boost::mutex syncher;
|
2176
|
-
SpawnerConfigPtr config;
|
2177
|
-
DummySpawnerPtr dummySpawner;
|
2178
|
-
|
2179
|
-
SpawnerPtr tryCreateSmartSpawner(const Options &options) {
|
2180
|
-
string dir = resourceLocator.getHelperScriptsDir();
|
2181
|
-
vector<string> preloaderCommand;
|
2182
|
-
if (options.appType == "classic-rails") {
|
2183
|
-
preloaderCommand.push_back(options.ruby);
|
2184
|
-
preloaderCommand.push_back(dir + "/classic-rails-preloader.rb");
|
2185
|
-
} else if (options.appType == "rack") {
|
2186
|
-
preloaderCommand.push_back(options.ruby);
|
2187
|
-
preloaderCommand.push_back(dir + "/rack-preloader.rb");
|
2188
|
-
} else {
|
2189
|
-
return SpawnerPtr();
|
2190
|
-
}
|
2191
|
-
return make_shared<SmartSpawner>(libev, resourceLocator,
|
2192
|
-
generation, preloaderCommand, options,
|
2193
|
-
randomGenerator);
|
2194
|
-
}
|
2195
|
-
|
2196
|
-
public:
|
2197
|
-
SpawnerFactory(const SafeLibevPtr &_libev,
|
2198
|
-
const ResourceLocator &_resourceLocator,
|
2199
|
-
const ServerInstanceDir::GenerationPtr &_generation,
|
2200
|
-
const RandomGeneratorPtr &_randomGenerator = RandomGeneratorPtr(),
|
2201
|
-
const SpawnerConfigPtr &_config = SpawnerConfigPtr())
|
2202
|
-
: libev(_libev),
|
2203
|
-
resourceLocator(_resourceLocator),
|
2204
|
-
generation(_generation)
|
2205
|
-
{
|
2206
|
-
if (_randomGenerator == NULL) {
|
2207
|
-
randomGenerator = make_shared<RandomGenerator>();
|
2208
|
-
} else {
|
2209
|
-
randomGenerator = _randomGenerator;
|
2210
|
-
}
|
2211
|
-
if (_config == NULL) {
|
2212
|
-
config = make_shared<SpawnerConfig>();
|
2213
|
-
} else {
|
2214
|
-
config = _config;
|
2215
|
-
}
|
2216
|
-
}
|
2217
|
-
|
2218
|
-
virtual ~SpawnerFactory() { }
|
2219
|
-
|
2220
|
-
virtual SpawnerPtr create(const Options &options) {
|
2221
|
-
if (options.spawnMethod == "smart" || options.spawnMethod == "smart-lv2") {
|
2222
|
-
SpawnerPtr spawner = tryCreateSmartSpawner(options);
|
2223
|
-
if (spawner == NULL) {
|
2224
|
-
spawner = make_shared<DirectSpawner>(libev,
|
2225
|
-
resourceLocator, generation,
|
2226
|
-
randomGenerator, config);
|
2227
|
-
}
|
2228
|
-
return spawner;
|
2229
|
-
} else if (options.spawnMethod == "direct" || options.spawnMethod == "conservative") {
|
2230
|
-
shared_ptr<DirectSpawner> spawner = make_shared<DirectSpawner>(libev, resourceLocator,
|
2231
|
-
generation, randomGenerator, config);
|
2232
|
-
return spawner;
|
2233
|
-
} else if (options.spawnMethod == "dummy") {
|
2234
|
-
syscalls::usleep(config->spawnerCreationSleepTime);
|
2235
|
-
return getDummySpawner();
|
2236
|
-
} else {
|
2237
|
-
throw ArgumentException("Unknown spawn method '" + options.spawnMethod + "'");
|
2238
|
-
}
|
2239
|
-
}
|
2240
|
-
|
2241
|
-
/**
|
2242
|
-
* SpawnerFactory always returns the same DummyFactory object upon
|
2243
|
-
* creating a dummy spawner. This allows unit tests to easily
|
2244
|
-
* set debugging options on the spawner.
|
2245
|
-
*/
|
2246
|
-
DummySpawnerPtr getDummySpawner() {
|
2247
|
-
lock_guard<boost::mutex> l(syncher);
|
2248
|
-
if (dummySpawner == NULL) {
|
2249
|
-
dummySpawner = make_shared<DummySpawner>(resourceLocator, config);
|
2250
|
-
}
|
2251
|
-
return dummySpawner;
|
2252
|
-
}
|
2253
|
-
|
2254
|
-
/**
|
2255
|
-
* All created Spawner objects share the same SpawnerConfig object.
|
2256
|
-
*/
|
2257
|
-
SpawnerConfigPtr getConfig() const {
|
2258
|
-
return config;
|
2259
|
-
}
|
2260
|
-
};
|
2261
|
-
|
2262
|
-
typedef shared_ptr<SpawnerFactory> SpawnerFactoryPtr;
|
2263
|
-
|
2264
|
-
|
2265
1257
|
} // namespace ApplicationPool2
|
2266
1258
|
} // namespace Passenger
|
2267
1259
|
|