passenger 5.0.9 → 5.0.10
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of passenger might be problematic. Click here for more details.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/CHANGELOG +15 -0
- data/CONTRIBUTORS +6 -0
- data/README.md +1 -1
- data/bin/passenger-install-apache2-module +24 -11
- data/bin/passenger-status +29 -14
- data/build/agents.rb +12 -10
- data/build/cxx_tests.rb +30 -30
- data/doc/Design and Architecture.html +1 -10
- data/doc/Design and Architecture.txt +1 -6
- data/doc/Users guide Apache.html +1 -19
- data/doc/Users guide Apache.txt +1 -1
- data/doc/Users guide Nginx.html +2 -20
- data/doc/Users guide Nginx.txt +2 -2
- data/doc/users_guide_snippets/tips.txt +0 -9
- data/ext/common/ApplicationPool2/ApiKey.h +158 -0
- data/ext/common/ApplicationPool2/BasicGroupInfo.h +81 -0
- data/ext/common/ApplicationPool2/BasicProcessInfo.h +106 -0
- data/ext/common/ApplicationPool2/Common.h +5 -44
- data/ext/common/ApplicationPool2/Context.h +94 -0
- data/ext/common/ApplicationPool2/Group.h +130 -1205
- data/ext/common/ApplicationPool2/Group/InitializationAndShutdown.cpp +190 -0
- data/ext/common/ApplicationPool2/Group/InternalUtils.cpp +329 -0
- data/ext/common/ApplicationPool2/Group/LifetimeAndBasics.cpp +103 -0
- data/ext/common/ApplicationPool2/{Pool/Debug.h → Group/Miscellaneous.cpp} +40 -38
- data/ext/common/ApplicationPool2/Group/OutOfBandWork.cpp +323 -0
- data/ext/common/ApplicationPool2/Group/ProcessListManagement.cpp +606 -0
- data/ext/common/ApplicationPool2/Group/SessionManagement.cpp +337 -0
- data/ext/common/ApplicationPool2/Group/SpawningAndRestarting.cpp +478 -0
- data/ext/common/ApplicationPool2/Group/StateInspection.cpp +197 -0
- data/ext/common/ApplicationPool2/Group/Verification.cpp +159 -0
- data/ext/common/ApplicationPool2/Implementation.cpp +19 -1401
- data/ext/common/ApplicationPool2/Options.h +5 -5
- data/ext/common/ApplicationPool2/Pool.h +260 -815
- data/ext/common/ApplicationPool2/Pool/{AnalyticsCollection.h → AnalyticsCollection.cpp} +55 -56
- data/ext/common/ApplicationPool2/Pool/{GarbageCollection.h → GarbageCollection.cpp} +49 -49
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.cpp +241 -0
- data/ext/common/ApplicationPool2/Pool/GroupUtils.cpp +276 -0
- data/ext/common/ApplicationPool2/Pool/InitializationAndShutdown.cpp +145 -0
- data/ext/common/ApplicationPool2/Pool/Miscellaneous.cpp +244 -0
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.cpp +330 -0
- data/ext/common/ApplicationPool2/Pool/StateInspection.cpp +299 -0
- data/ext/common/ApplicationPool2/Process.h +399 -205
- data/ext/common/ApplicationPool2/Session.h +70 -28
- data/ext/common/ApplicationPool2/Socket.h +1 -0
- data/ext/common/Constants.h +11 -3
- data/ext/common/Exceptions.h +1 -1
- data/ext/common/Logging.cpp +9 -4
- data/ext/common/Logging.h +6 -0
- data/ext/common/ServerKit/HttpServer.h +225 -215
- data/ext/common/ServerKit/Server.h +57 -57
- data/ext/common/SpawningKit/BackgroundIOCapturer.h +160 -0
- data/ext/common/SpawningKit/Config.h +107 -0
- data/ext/common/{ApplicationPool2 → SpawningKit}/DirectSpawner.h +17 -16
- data/ext/common/{ApplicationPool2 → SpawningKit}/DummySpawner.h +33 -33
- data/ext/common/{ApplicationPool2/SpawnerFactory.h → SpawningKit/Factory.h} +17 -17
- data/ext/common/{ApplicationPool2/ComponentInfo.h → SpawningKit/Options.h} +8 -21
- data/ext/common/SpawningKit/PipeWatcher.h +148 -0
- data/ext/common/{ApplicationPool2/PipeWatcher.h → SpawningKit/Result.h} +15 -33
- data/ext/common/{ApplicationPool2 → SpawningKit}/SmartSpawner.h +52 -57
- data/ext/common/{ApplicationPool2 → SpawningKit}/Spawner.h +83 -371
- data/ext/common/SpawningKit/UserSwitchingRules.h +265 -0
- data/ext/common/Utils/BufferedIO.h +24 -0
- data/ext/common/{ApplicationPool2/SpawnObject.h → Utils/ClassUtils.h} +24 -51
- data/ext/common/Utils/IOUtils.cpp +70 -0
- data/ext/common/Utils/IOUtils.h +19 -0
- data/ext/common/Utils/JsonUtils.h +113 -0
- data/ext/common/Utils/StrIntUtils.h +29 -0
- data/ext/common/Utils/json.h +1 -1
- data/ext/common/agents/ApiServerUtils.h +941 -0
- data/ext/common/agents/HelperAgent/{AdminServer.h → ApiServer.h} +163 -365
- data/ext/common/agents/HelperAgent/Main.cpp +86 -88
- data/ext/common/agents/HelperAgent/OptionParser.h +9 -10
- data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -0
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +2 -0
- data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -1
- data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +2 -2
- data/ext/common/agents/LoggingAgent/ApiServer.h +279 -0
- data/ext/common/agents/LoggingAgent/Main.cpp +41 -51
- data/ext/common/agents/LoggingAgent/OptionParser.h +11 -11
- data/ext/common/agents/Watchdog/ApiServer.h +311 -0
- data/ext/common/agents/Watchdog/Main.cpp +91 -65
- data/helper-scripts/prespawn +2 -0
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/admin_tools/instance.rb +1 -1
- data/lib/phusion_passenger/common_library.rb +27 -14
- data/lib/phusion_passenger/config/{admin_command_command.rb → api_call_command.rb} +19 -16
- data/lib/phusion_passenger/config/detach_process_command.rb +6 -3
- data/lib/phusion_passenger/config/main.rb +3 -5
- data/lib/phusion_passenger/config/reopen_logs_command.rb +29 -7
- data/lib/phusion_passenger/config/restart_app_command.rb +13 -4
- data/lib/phusion_passenger/config/utils.rb +15 -8
- data/lib/phusion_passenger/constants.rb +6 -2
- data/lib/phusion_passenger/platform_info/apache.rb +4 -0
- data/lib/phusion_passenger/platform_info/apache_detector.rb +18 -3
- data/resources/templates/apache2/mpm_unknown.txt.erb +20 -0
- metadata +42 -21
- metadata.gz.asc +7 -7
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.h +0 -127
- data/ext/common/ApplicationPool2/Pool/Inspection.h +0 -219
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.h +0 -85
- data/ext/common/ApplicationPool2/SuperGroup.h +0 -706
- data/ext/common/agents/LoggingAgent/AdminServer.h +0 -435
- data/ext/common/agents/Watchdog/AdminServer.h +0 -432
@@ -22,8 +22,8 @@
|
|
22
22
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
23
|
* THE SOFTWARE.
|
24
24
|
*/
|
25
|
-
#ifndef
|
26
|
-
#define
|
25
|
+
#ifndef _PASSENGER_SPAWNING_KIT_SPAWNER_H_
|
26
|
+
#define _PASSENGER_SPAWNING_KIT_SPAWNER_H_
|
27
27
|
|
28
28
|
/*
|
29
29
|
* This file implements application spawning support. Several classes
|
@@ -77,11 +77,11 @@
|
|
77
77
|
#include <pwd.h>
|
78
78
|
#include <grp.h>
|
79
79
|
#include <dirent.h>
|
80
|
-
#include <
|
81
|
-
#include <
|
82
|
-
#include <
|
83
|
-
#include <
|
84
|
-
#include <
|
80
|
+
#include <SpawningKit/Config.h>
|
81
|
+
#include <SpawningKit/Options.h>
|
82
|
+
#include <SpawningKit/Result.h>
|
83
|
+
#include <SpawningKit/BackgroundIOCapturer.h>
|
84
|
+
#include <SpawningKit/UserSwitchingRules.h>
|
85
85
|
#include <FileDescriptor.h>
|
86
86
|
#include <Exceptions.h>
|
87
87
|
#include <StaticString.h>
|
@@ -100,7 +100,7 @@ namespace tut {
|
|
100
100
|
}
|
101
101
|
|
102
102
|
namespace Passenger {
|
103
|
-
namespace
|
103
|
+
namespace SpawningKit {
|
104
104
|
|
105
105
|
using namespace std;
|
106
106
|
using namespace boost;
|
@@ -112,111 +112,6 @@ protected:
|
|
112
112
|
friend struct tut::ApplicationPool2_DirectSpawnerTest;
|
113
113
|
friend struct tut::ApplicationPool2_SmartSpawnerTest;
|
114
114
|
|
115
|
-
/**
|
116
|
-
* Given a file descriptor, captures its output in a background thread
|
117
|
-
* and also forwards it immediately to a target file descriptor.
|
118
|
-
* Call stop() to stop the background thread and to obtain the captured
|
119
|
-
* output so far.
|
120
|
-
*/
|
121
|
-
class BackgroundIOCapturer {
|
122
|
-
private:
|
123
|
-
FileDescriptor fd;
|
124
|
-
pid_t pid;
|
125
|
-
const char *channelName;
|
126
|
-
boost::mutex dataSyncher;
|
127
|
-
string data;
|
128
|
-
oxt::thread *thr;
|
129
|
-
|
130
|
-
void capture() {
|
131
|
-
TRACE_POINT();
|
132
|
-
while (!this_thread::interruption_requested()) {
|
133
|
-
char buf[1024 * 8];
|
134
|
-
ssize_t ret;
|
135
|
-
|
136
|
-
UPDATE_TRACE_POINT();
|
137
|
-
ret = syscalls::read(fd, buf, sizeof(buf));
|
138
|
-
int e = errno;
|
139
|
-
this_thread::disable_syscall_interruption dsi;
|
140
|
-
if (ret == 0) {
|
141
|
-
break;
|
142
|
-
} else if (ret == -1) {
|
143
|
-
if (e != EAGAIN && e != EWOULDBLOCK) {
|
144
|
-
P_WARN("Background I/O capturer error: " <<
|
145
|
-
strerror(e) << " (errno=" << e << ")");
|
146
|
-
break;
|
147
|
-
}
|
148
|
-
} else {
|
149
|
-
{
|
150
|
-
boost::lock_guard<boost::mutex> l(dataSyncher);
|
151
|
-
data.append(buf, ret);
|
152
|
-
}
|
153
|
-
UPDATE_TRACE_POINT();
|
154
|
-
if (ret == 1 && buf[0] == '\n') {
|
155
|
-
printAppOutput(pid, channelName, "", 0);
|
156
|
-
} else {
|
157
|
-
vector<StaticString> lines;
|
158
|
-
if (ret > 0 && buf[ret - 1] == '\n') {
|
159
|
-
ret--;
|
160
|
-
}
|
161
|
-
split(StaticString(buf, ret), '\n', lines);
|
162
|
-
foreach (const StaticString line, lines) {
|
163
|
-
printAppOutput(pid, channelName, line.data(), line.size());
|
164
|
-
}
|
165
|
-
}
|
166
|
-
}
|
167
|
-
}
|
168
|
-
}
|
169
|
-
|
170
|
-
public:
|
171
|
-
BackgroundIOCapturer(const FileDescriptor &_fd, pid_t _pid, const char *_channelName)
|
172
|
-
: fd(_fd),
|
173
|
-
pid(_pid),
|
174
|
-
channelName(_channelName),
|
175
|
-
thr(NULL)
|
176
|
-
{ }
|
177
|
-
|
178
|
-
~BackgroundIOCapturer() {
|
179
|
-
TRACE_POINT();
|
180
|
-
if (thr != NULL) {
|
181
|
-
this_thread::disable_interruption di;
|
182
|
-
this_thread::disable_syscall_interruption dsi;
|
183
|
-
thr->interrupt_and_join();
|
184
|
-
delete thr;
|
185
|
-
thr = NULL;
|
186
|
-
}
|
187
|
-
}
|
188
|
-
|
189
|
-
const FileDescriptor &getFd() const {
|
190
|
-
return fd;
|
191
|
-
}
|
192
|
-
|
193
|
-
void start() {
|
194
|
-
assert(thr == NULL);
|
195
|
-
thr = new oxt::thread(boost::bind(&BackgroundIOCapturer::capture, this),
|
196
|
-
"Background I/O capturer", 64 * 1024);
|
197
|
-
}
|
198
|
-
|
199
|
-
string stop() {
|
200
|
-
TRACE_POINT();
|
201
|
-
assert(thr != NULL);
|
202
|
-
this_thread::disable_interruption di;
|
203
|
-
this_thread::disable_syscall_interruption dsi;
|
204
|
-
thr->interrupt_and_join();
|
205
|
-
delete thr;
|
206
|
-
thr = NULL;
|
207
|
-
boost::lock_guard<boost::mutex> l(dataSyncher);
|
208
|
-
return data;
|
209
|
-
}
|
210
|
-
|
211
|
-
void appendToBuffer(const StaticString &dataToAdd) {
|
212
|
-
TRACE_POINT();
|
213
|
-
boost::lock_guard<boost::mutex> l(dataSyncher);
|
214
|
-
data.append(dataToAdd.data(), dataToAdd.size());
|
215
|
-
}
|
216
|
-
};
|
217
|
-
|
218
|
-
typedef boost::shared_ptr<BackgroundIOCapturer> BackgroundIOCapturerPtr;
|
219
|
-
|
220
115
|
/**
|
221
116
|
* A temporary directory for spawned child processes to write
|
222
117
|
* debugging information to. It is removed after spawning has
|
@@ -311,16 +206,7 @@ protected:
|
|
311
206
|
*/
|
312
207
|
vector<string> appRootPathsInsideChroot;
|
313
208
|
|
314
|
-
|
315
|
-
bool switchUser;
|
316
|
-
string username;
|
317
|
-
string groupname;
|
318
|
-
string home;
|
319
|
-
string shell;
|
320
|
-
uid_t uid;
|
321
|
-
gid_t gid;
|
322
|
-
int ngroups;
|
323
|
-
shared_array<gid_t> gidset;
|
209
|
+
UserSwitchingInfo userSwitching;
|
324
210
|
|
325
211
|
// Other information
|
326
212
|
string codeRevision;
|
@@ -365,7 +251,6 @@ protected:
|
|
365
251
|
}
|
366
252
|
};
|
367
253
|
|
368
|
-
|
369
254
|
private:
|
370
255
|
/**
|
371
256
|
* Appends key + "\0" + value + "\0" to 'output'.
|
@@ -389,10 +274,11 @@ private:
|
|
389
274
|
"ruby_libdir: " + config->resourceLocator->getRubyLibDir() + "\n"
|
390
275
|
"gupid: " + details.gupid + "\n"
|
391
276
|
"UNIX_PATH_MAX: " + toString(UNIX_PATH_MAX) + "\n";
|
392
|
-
if (!details.options->
|
393
|
-
data.append("connect_password: " + details.options->
|
277
|
+
if (!details.options->apiKey.empty()) {
|
278
|
+
data.append("connect_password: " + details.options->apiKey + "\n");
|
394
279
|
}
|
395
280
|
if (!config->instanceDir.empty()) {
|
281
|
+
data.append("instance_dir: " + config->instanceDir + "\n");
|
396
282
|
data.append("socket_dir: " + config->instanceDir + "/apps.s\n");
|
397
283
|
}
|
398
284
|
|
@@ -425,10 +311,10 @@ private:
|
|
425
311
|
}
|
426
312
|
}
|
427
313
|
|
428
|
-
|
314
|
+
Result handleSpawnResponse(NegotiationDetails &details) {
|
429
315
|
TRACE_POINT();
|
430
|
-
|
431
|
-
|
316
|
+
Json::Value sockets;
|
317
|
+
Result result;
|
432
318
|
|
433
319
|
while (true) {
|
434
320
|
string line;
|
@@ -491,12 +377,12 @@ private:
|
|
491
377
|
details);
|
492
378
|
}
|
493
379
|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
sockets.
|
380
|
+
Json::Value socket;
|
381
|
+
socket["name"] = args[0];
|
382
|
+
socket["address"] = fixupSocketAddress(*details.options, args[1]);
|
383
|
+
socket["protocol"] = args[2];
|
384
|
+
socket["concurrency"] = atoi(args[3]);
|
385
|
+
sockets.append(socket);
|
500
386
|
} else {
|
501
387
|
throwAppSpawnException("An error occurred while starting the "
|
502
388
|
"web application. It reported a wrongly formatted 'socket'"
|
@@ -512,7 +398,7 @@ private:
|
|
512
398
|
|
513
399
|
pids.push_back(pid);
|
514
400
|
ProcessMetricMap metrics = collector.collect(pids);
|
515
|
-
if (metrics[pid].uid != details.preparation->uid) {
|
401
|
+
if (metrics[pid].uid != details.preparation->userSwitching.uid) {
|
516
402
|
throwAppSpawnException("An error occurred while starting the "
|
517
403
|
"web application. The PID that the loader has returned does "
|
518
404
|
"not have the same UID as the loader itself.",
|
@@ -529,25 +415,39 @@ private:
|
|
529
415
|
}
|
530
416
|
}
|
531
417
|
|
532
|
-
if (!
|
418
|
+
if (!hasSessionSockets(sockets)) {
|
533
419
|
throwAppSpawnException("An error occured while starting the web "
|
534
420
|
"application. It did not advertise any session sockets.",
|
535
421
|
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
536
422
|
details);
|
537
423
|
}
|
538
424
|
|
539
|
-
result
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
result
|
545
|
-
|
546
|
-
|
425
|
+
result["type"] = "os_process";
|
426
|
+
result["pid"] = (int) details.pid;
|
427
|
+
result["gupid"] = details.gupid;
|
428
|
+
result["sockets"] = sockets;
|
429
|
+
result["code_revision"] = details.preparation->codeRevision;
|
430
|
+
result["spawner_creation_time"] = (Json::UInt64) creationTime;
|
431
|
+
result["spawn_start_time"] = (Json::UInt64) details.spawnStartTime;
|
432
|
+
result.adminSocket = details.adminSocket;
|
433
|
+
result.errorPipe = details.errorPipe;
|
434
|
+
return result;
|
435
|
+
}
|
436
|
+
|
437
|
+
bool hasSessionSockets(const Json::Value &sockets) const {
|
438
|
+
Json::Value::const_iterator it, end = sockets.end();
|
439
|
+
|
440
|
+
for (it = sockets.begin(); it != end; it++) {
|
441
|
+
const Json::Value &socket = *it;
|
442
|
+
if (socket["protocol"] == "session" || socket["protocol"] == "http_session") {
|
443
|
+
return true;
|
444
|
+
}
|
445
|
+
}
|
446
|
+
return false;
|
547
447
|
}
|
548
448
|
|
549
449
|
protected:
|
550
|
-
|
450
|
+
ConfigPtr config;
|
551
451
|
|
552
452
|
static void nonInterruptableKillAndWaitpid(pid_t pid) {
|
553
453
|
this_thread::disable_syscall_interruption dsi;
|
@@ -646,9 +546,9 @@ protected:
|
|
646
546
|
e << ": " << strerror(e) << ")";
|
647
547
|
break;
|
648
548
|
}
|
649
|
-
if (buf.st_uid != details.preparation->uid) {
|
549
|
+
if (buf.st_uid != details.preparation->userSwitching.uid) {
|
650
550
|
error << "It advertised a Unix domain socket that has a different " <<
|
651
|
-
"owner than expected (should be UID " << details.preparation->uid <<
|
551
|
+
"owner than expected (should be UID " << details.preparation->userSwitching.uid <<
|
652
552
|
", but actual UID was " << buf.st_uid << ")";
|
653
553
|
break;
|
654
554
|
}
|
@@ -742,7 +642,9 @@ protected:
|
|
742
642
|
}
|
743
643
|
|
744
644
|
void throwSpawnException(SpawnException &e, const Options &options) {
|
745
|
-
|
645
|
+
if (config->errorHandler != NULL) {
|
646
|
+
config->errorHandler(config, e, options);
|
647
|
+
}
|
746
648
|
throw e;
|
747
649
|
}
|
748
650
|
|
@@ -813,7 +715,7 @@ protected:
|
|
813
715
|
TRACE_POINT();
|
814
716
|
SpawnPreparationInfo info;
|
815
717
|
prepareChroot(info, options);
|
816
|
-
prepareUserSwitching(
|
718
|
+
info.userSwitching = prepareUserSwitching(options);
|
817
719
|
prepareSwitchingWorkingDirectory(info, options);
|
818
720
|
inferApplicationInfo(info);
|
819
721
|
return info;
|
@@ -843,201 +745,6 @@ protected:
|
|
843
745
|
}
|
844
746
|
}
|
845
747
|
|
846
|
-
void prepareUserSwitching(SpawnPreparationInfo &info, const Options &options) const {
|
847
|
-
TRACE_POINT();
|
848
|
-
if (geteuid() != 0) {
|
849
|
-
struct passwd pwd, *userInfo;
|
850
|
-
long bufSize;
|
851
|
-
shared_array<char> strings;
|
852
|
-
|
853
|
-
// _SC_GETPW_R_SIZE_MAX is not a maximum:
|
854
|
-
// http://tomlee.co/2012/10/problems-with-large-linux-unix-groups-and-getgrgid_r-getgrnam_r/
|
855
|
-
bufSize = std::max<long>(1024 * 128, sysconf(_SC_GETPW_R_SIZE_MAX));
|
856
|
-
strings.reset(new char[bufSize]);
|
857
|
-
|
858
|
-
userInfo = (struct passwd *) NULL;
|
859
|
-
if (getpwuid_r(geteuid(), &pwd, strings.get(), bufSize, &userInfo) != 0
|
860
|
-
|| userInfo == (struct passwd *) NULL)
|
861
|
-
{
|
862
|
-
throw RuntimeException("Cannot get user database entry for user " +
|
863
|
-
getProcessUsername() + "; it looks like your system's " +
|
864
|
-
"user database is broken, please fix it.");
|
865
|
-
}
|
866
|
-
|
867
|
-
info.switchUser = false;
|
868
|
-
info.username = userInfo->pw_name;
|
869
|
-
info.groupname = getGroupName(userInfo->pw_gid);
|
870
|
-
info.home = userInfo->pw_dir;
|
871
|
-
info.shell = userInfo->pw_shell;
|
872
|
-
info.uid = geteuid();
|
873
|
-
info.gid = getegid();
|
874
|
-
info.ngroups = 0;
|
875
|
-
return;
|
876
|
-
}
|
877
|
-
|
878
|
-
UPDATE_TRACE_POINT();
|
879
|
-
string defaultGroup;
|
880
|
-
string startupFile = absolutizePath(options.getStartupFile(), info.appRoot);
|
881
|
-
struct passwd pwd, *userInfo;
|
882
|
-
struct group grp;
|
883
|
-
gid_t groupId = (gid_t) -1;
|
884
|
-
long pwdBufSize, grpBufSize;
|
885
|
-
shared_array<char> pwdBuf, grpBuf;
|
886
|
-
int ret;
|
887
|
-
|
888
|
-
// _SC_GETPW_R_SIZE_MAX/_SC_GETGR_R_SIZE_MAX are not maximums:
|
889
|
-
// http://tomlee.co/2012/10/problems-with-large-linux-unix-groups-and-getgrgid_r-getgrnam_r/
|
890
|
-
pwdBufSize = std::max<long>(1024 * 128, sysconf(_SC_GETPW_R_SIZE_MAX));
|
891
|
-
pwdBuf.reset(new char[pwdBufSize]);
|
892
|
-
grpBufSize = std::max<long>(1024 * 128, sysconf(_SC_GETGR_R_SIZE_MAX));
|
893
|
-
grpBuf.reset(new char[grpBufSize]);
|
894
|
-
|
895
|
-
if (options.defaultGroup.empty()) {
|
896
|
-
struct passwd *info;
|
897
|
-
struct group *group;
|
898
|
-
|
899
|
-
info = (struct passwd *) NULL;
|
900
|
-
ret = getpwnam_r(options.defaultUser.c_str(), &pwd, pwdBuf.get(),
|
901
|
-
pwdBufSize, &info);
|
902
|
-
if (ret != 0) {
|
903
|
-
info = (struct passwd *) NULL;
|
904
|
-
}
|
905
|
-
if (info == (struct passwd *) NULL) {
|
906
|
-
throw RuntimeException("Cannot get user database entry for username '" +
|
907
|
-
options.defaultUser + "'");
|
908
|
-
}
|
909
|
-
|
910
|
-
group = (struct group *) NULL;
|
911
|
-
ret = getgrgid_r(info->pw_gid, &grp, grpBuf.get(), grpBufSize, &group);
|
912
|
-
if (ret != 0) {
|
913
|
-
group = (struct group *) NULL;
|
914
|
-
}
|
915
|
-
if (group == (struct group *) NULL) {
|
916
|
-
throw RuntimeException(string("Cannot get group database entry for ") +
|
917
|
-
"the default group belonging to username '" +
|
918
|
-
options.defaultUser + "'");
|
919
|
-
}
|
920
|
-
defaultGroup = group->gr_name;
|
921
|
-
} else {
|
922
|
-
defaultGroup = options.defaultGroup;
|
923
|
-
}
|
924
|
-
|
925
|
-
UPDATE_TRACE_POINT();
|
926
|
-
userInfo = (struct passwd *) NULL;
|
927
|
-
if (!options.userSwitching) {
|
928
|
-
// Keep userInfo at NULL so that it's set to defaultUser's UID.
|
929
|
-
} else if (!options.user.empty()) {
|
930
|
-
ret = getpwnam_r(options.user.c_str(), &pwd, pwdBuf.get(),
|
931
|
-
pwdBufSize, &userInfo);
|
932
|
-
if (ret != 0) {
|
933
|
-
userInfo = (struct passwd *) NULL;
|
934
|
-
}
|
935
|
-
} else {
|
936
|
-
struct stat buf;
|
937
|
-
if (syscalls::lstat(startupFile.c_str(), &buf) == -1) {
|
938
|
-
int e = errno;
|
939
|
-
throw SystemException("Cannot lstat(\"" + startupFile +
|
940
|
-
"\")", e);
|
941
|
-
}
|
942
|
-
ret = getpwuid_r(buf.st_uid, &pwd, pwdBuf.get(),
|
943
|
-
pwdBufSize, &userInfo);
|
944
|
-
if (ret != 0) {
|
945
|
-
userInfo = (struct passwd *) NULL;
|
946
|
-
}
|
947
|
-
}
|
948
|
-
if (userInfo == (struct passwd *) NULL || userInfo->pw_uid == 0) {
|
949
|
-
userInfo = (struct passwd *) NULL;
|
950
|
-
ret = getpwnam_r(options.defaultUser.c_str(), &pwd,
|
951
|
-
pwdBuf.get(), pwdBufSize, &userInfo);
|
952
|
-
if (ret != 0) {
|
953
|
-
userInfo = (struct passwd *) NULL;
|
954
|
-
}
|
955
|
-
}
|
956
|
-
|
957
|
-
UPDATE_TRACE_POINT();
|
958
|
-
if (!options.userSwitching) {
|
959
|
-
// Keep groupId at -1 so that it's set to defaultGroup's GID.
|
960
|
-
} else if (!options.group.empty()) {
|
961
|
-
struct group *groupInfo = (struct group *) NULL;
|
962
|
-
|
963
|
-
if (options.group == "!STARTUP_FILE!") {
|
964
|
-
struct stat buf;
|
965
|
-
|
966
|
-
if (syscalls::lstat(startupFile.c_str(), &buf) == -1) {
|
967
|
-
int e = errno;
|
968
|
-
throw SystemException("Cannot lstat(\"" +
|
969
|
-
startupFile + "\")", e);
|
970
|
-
}
|
971
|
-
|
972
|
-
ret = getgrgid_r(buf.st_gid, &grp, grpBuf.get(), grpBufSize,
|
973
|
-
&groupInfo);
|
974
|
-
if (ret != 0) {
|
975
|
-
groupInfo = (struct group *) NULL;
|
976
|
-
}
|
977
|
-
if (groupInfo != NULL) {
|
978
|
-
groupId = buf.st_gid;
|
979
|
-
} else {
|
980
|
-
groupId = (gid_t) -1;
|
981
|
-
}
|
982
|
-
} else {
|
983
|
-
ret = getgrnam_r(options.group.c_str(), &grp, grpBuf.get(),
|
984
|
-
grpBufSize, &groupInfo);
|
985
|
-
if (ret != 0) {
|
986
|
-
groupInfo = (struct group *) NULL;
|
987
|
-
}
|
988
|
-
if (groupInfo != NULL) {
|
989
|
-
groupId = groupInfo->gr_gid;
|
990
|
-
} else {
|
991
|
-
groupId = (gid_t) -1;
|
992
|
-
}
|
993
|
-
}
|
994
|
-
} else if (userInfo != (struct passwd *) NULL) {
|
995
|
-
groupId = userInfo->pw_gid;
|
996
|
-
}
|
997
|
-
if (groupId == 0 || groupId == (gid_t) -1) {
|
998
|
-
groupId = lookupGid(defaultGroup);
|
999
|
-
}
|
1000
|
-
|
1001
|
-
UPDATE_TRACE_POINT();
|
1002
|
-
if (userInfo == (struct passwd *) NULL) {
|
1003
|
-
throw RuntimeException("Cannot determine a user to lower privilege to");
|
1004
|
-
}
|
1005
|
-
if (groupId == (gid_t) -1) {
|
1006
|
-
throw RuntimeException("Cannot determine a group to lower privilege to");
|
1007
|
-
}
|
1008
|
-
|
1009
|
-
UPDATE_TRACE_POINT();
|
1010
|
-
#ifdef __APPLE__
|
1011
|
-
int groups[1024];
|
1012
|
-
info.ngroups = sizeof(groups) / sizeof(int);
|
1013
|
-
#else
|
1014
|
-
gid_t groups[1024];
|
1015
|
-
info.ngroups = sizeof(groups) / sizeof(gid_t);
|
1016
|
-
#endif
|
1017
|
-
info.switchUser = true;
|
1018
|
-
info.username = userInfo->pw_name;
|
1019
|
-
info.groupname = getGroupName(groupId);
|
1020
|
-
info.home = userInfo->pw_dir;
|
1021
|
-
info.shell = userInfo->pw_shell;
|
1022
|
-
info.uid = userInfo->pw_uid;
|
1023
|
-
info.gid = groupId;
|
1024
|
-
#if !defined(HAVE_GETGROUPLIST) && (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__))
|
1025
|
-
#define HAVE_GETGROUPLIST
|
1026
|
-
#endif
|
1027
|
-
#ifdef HAVE_GETGROUPLIST
|
1028
|
-
ret = getgrouplist(userInfo->pw_name, groupId,
|
1029
|
-
groups, &info.ngroups);
|
1030
|
-
if (ret == -1) {
|
1031
|
-
int e = errno;
|
1032
|
-
throw SystemException("getgrouplist() failed", e);
|
1033
|
-
}
|
1034
|
-
info.gidset = shared_array<gid_t>(new gid_t[info.ngroups]);
|
1035
|
-
for (int i = 0; i < info.ngroups; i++) {
|
1036
|
-
info.gidset[i] = groups[i];
|
1037
|
-
}
|
1038
|
-
#endif
|
1039
|
-
}
|
1040
|
-
|
1041
748
|
void prepareSwitchingWorkingDirectory(SpawnPreparationInfo &info, const Options &options) const {
|
1042
749
|
vector<string> components;
|
1043
750
|
split(info.appRootInsideChroot, '/', components);
|
@@ -1109,7 +816,7 @@ protected:
|
|
1109
816
|
|
1110
817
|
bool shouldLoadShellEnvvars(const Options &options, const SpawnPreparationInfo &preparation) const {
|
1111
818
|
if (options.loadShellEnvvars) {
|
1112
|
-
string shellName = extractBaseName(preparation.shell);
|
819
|
+
string shellName = extractBaseName(preparation.userSwitching.shell);
|
1113
820
|
return shellName == "bash" || shellName == "zsh" || shellName == "ksh";
|
1114
821
|
} else {
|
1115
822
|
return false;
|
@@ -1164,23 +871,27 @@ protected:
|
|
1164
871
|
}
|
1165
872
|
|
1166
873
|
void switchUser(const SpawnPreparationInfo &info) {
|
1167
|
-
if (info.
|
874
|
+
if (info.userSwitching.enabled) {
|
1168
875
|
bool setgroupsCalled = false;
|
1169
876
|
#ifdef HAVE_GETGROUPLIST
|
1170
|
-
if (info.ngroups <= NGROUPS_MAX) {
|
877
|
+
if (info.userSwitching.ngroups <= NGROUPS_MAX) {
|
1171
878
|
setgroupsCalled = true;
|
1172
|
-
if (setgroups(info.ngroups,
|
879
|
+
if (setgroups(info.userSwitching.ngroups,
|
880
|
+
info.userSwitching.gidset.get()) == -1)
|
881
|
+
{
|
1173
882
|
int e = errno;
|
1174
883
|
printf("!> Error\n");
|
1175
884
|
printf("!> \n");
|
1176
885
|
printf("setgroups(%d, ...) failed: %s (errno=%d)\n",
|
1177
|
-
info.ngroups, strerror(e), e);
|
886
|
+
info.userSwitching.ngroups, strerror(e), e);
|
1178
887
|
fflush(stdout);
|
1179
888
|
_exit(1);
|
1180
889
|
}
|
1181
890
|
}
|
1182
891
|
#endif
|
1183
|
-
if (!setgroupsCalled && initgroups(info.username.c_str(),
|
892
|
+
if (!setgroupsCalled && initgroups(info.userSwitching.username.c_str(),
|
893
|
+
info.userSwitching.gid) == -1)
|
894
|
+
{
|
1184
895
|
int e = errno;
|
1185
896
|
printf("!> Error\n");
|
1186
897
|
printf("!> \n");
|
@@ -1189,7 +900,7 @@ protected:
|
|
1189
900
|
fflush(stdout);
|
1190
901
|
_exit(1);
|
1191
902
|
}
|
1192
|
-
if (setgid(info.gid) == -1) {
|
903
|
+
if (setgid(info.userSwitching.gid) == -1) {
|
1193
904
|
int e = errno;
|
1194
905
|
printf("!> Error\n");
|
1195
906
|
printf("!> \n");
|
@@ -1198,7 +909,7 @@ protected:
|
|
1198
909
|
fflush(stdout);
|
1199
910
|
_exit(1);
|
1200
911
|
}
|
1201
|
-
if (setuid(info.uid) == -1) {
|
912
|
+
if (setuid(info.userSwitching.uid) == -1) {
|
1202
913
|
int e = errno;
|
1203
914
|
printf("!> Error\n");
|
1204
915
|
printf("!> \n");
|
@@ -1212,10 +923,10 @@ protected:
|
|
1212
923
|
// in the SpawnPreparer because SpawnPreparer might
|
1213
924
|
// be executed by bash, but these environment variables
|
1214
925
|
// must be set before bash.
|
1215
|
-
setenv("USER", info.username.c_str(), 1);
|
1216
|
-
setenv("LOGNAME", info.username.c_str(), 1);
|
1217
|
-
setenv("SHELL", info.shell.c_str(), 1);
|
1218
|
-
setenv("HOME", info.home.c_str(), 1);
|
926
|
+
setenv("USER", info.userSwitching.username.c_str(), 1);
|
927
|
+
setenv("LOGNAME", info.userSwitching.username.c_str(), 1);
|
928
|
+
setenv("SHELL", info.userSwitching.shell.c_str(), 1);
|
929
|
+
setenv("HOME", info.userSwitching.home.c_str(), 1);
|
1219
930
|
}
|
1220
931
|
}
|
1221
932
|
|
@@ -1264,8 +975,8 @@ protected:
|
|
1264
975
|
"However, the parent directory '%s' has wrong permissions, thereby "
|
1265
976
|
"preventing this process from accessing its application root directory. "
|
1266
977
|
"Please fix the permissions of the directory '%s' first.\n",
|
1267
|
-
info.username.c_str(),
|
1268
|
-
info.groupname.c_str(),
|
978
|
+
info.userSwitching.username.c_str(),
|
979
|
+
info.userSwitching.groupname.c_str(),
|
1269
980
|
info.appRootPaths.back().c_str(),
|
1270
981
|
parent,
|
1271
982
|
parent);
|
@@ -1292,8 +1003,8 @@ protected:
|
|
1292
1003
|
"and must be able to access its application root directory '%s'. "
|
1293
1004
|
"However this directory is not accessible because it has wrong permissions. "
|
1294
1005
|
"Please fix these permissions first.\n",
|
1295
|
-
info.username.c_str(),
|
1296
|
-
info.groupname.c_str(),
|
1006
|
+
info.userSwitching.username.c_str(),
|
1007
|
+
info.userSwitching.groupname.c_str(),
|
1297
1008
|
info.appRootPaths.back().c_str());
|
1298
1009
|
fflush(stdout);
|
1299
1010
|
_exit(1);
|
@@ -1311,7 +1022,7 @@ protected:
|
|
1311
1022
|
/**
|
1312
1023
|
* Execute the process spawning negotiation protocol.
|
1313
1024
|
*/
|
1314
|
-
|
1025
|
+
Result negotiateSpawn(NegotiationDetails &details) {
|
1315
1026
|
TRACE_POINT();
|
1316
1027
|
details.spawnStartTime = SystemTime::getUsec();
|
1317
1028
|
details.gupid = integerToHex(SystemTime::get() / 60) + "-" +
|
@@ -1369,7 +1080,7 @@ protected:
|
|
1369
1080
|
handleInvalidSpawnResponseType(result, details);
|
1370
1081
|
}
|
1371
1082
|
}
|
1372
|
-
return
|
1083
|
+
return Result(); // Never reached.
|
1373
1084
|
}
|
1374
1085
|
|
1375
1086
|
void handleSpawnErrorResponse(NegotiationDetails &details) {
|
@@ -1454,34 +1165,35 @@ public:
|
|
1454
1165
|
*/
|
1455
1166
|
const unsigned long long creationTime;
|
1456
1167
|
|
1457
|
-
Spawner(const
|
1168
|
+
Spawner(const ConfigPtr &_config)
|
1458
1169
|
: config(_config),
|
1459
1170
|
creationTime(SystemTime::getUsec())
|
1460
1171
|
{ }
|
1461
1172
|
|
1462
1173
|
virtual ~Spawner() { }
|
1463
|
-
virtual SpawnObject spawn(const Options &options) = 0;
|
1464
1174
|
|
1465
|
-
|
1175
|
+
virtual Result spawn(const Options &options) = 0;
|
1176
|
+
|
1466
1177
|
virtual bool cleanable() const {
|
1467
1178
|
return false;
|
1468
1179
|
}
|
1469
1180
|
|
1470
|
-
virtual void cleanup() {
|
1181
|
+
virtual void cleanup() {
|
1182
|
+
// Do nothing.
|
1183
|
+
}
|
1471
1184
|
|
1472
|
-
/** Does not depend on the event loop. */
|
1473
1185
|
virtual unsigned long long lastUsed() const {
|
1474
1186
|
return 0;
|
1475
1187
|
}
|
1476
1188
|
|
1477
|
-
|
1189
|
+
ConfigPtr getConfig() const {
|
1478
1190
|
return config;
|
1479
1191
|
}
|
1480
1192
|
};
|
1481
1193
|
typedef boost::shared_ptr<Spawner> SpawnerPtr;
|
1482
1194
|
|
1483
1195
|
|
1484
|
-
} // namespace
|
1196
|
+
} // namespace SpawningKit
|
1485
1197
|
} // namespace Passenger
|
1486
1198
|
|
1487
|
-
#endif /*
|
1199
|
+
#endif /* _PASSENGER_SPAWNING_KIT_SPAWNER_H_ */
|