passenger 5.3.1 → 5.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +19 -0
- data/build/cxx_tests.rb +3 -1
- data/build/support/cxx_dependency_map.rb +120 -27
- data/dev/configkit-schemas/index.json +15 -3
- data/src/agent/Core/AdminPanelConnector.h +5 -2
- data/src/agent/Core/ApplicationPool/Group/StateInspection.cpp +2 -0
- data/src/agent/Core/Config.h +2 -1
- data/src/agent/Core/Controller/Config.h +6 -1
- data/src/agent/Core/Controller/InitRequest.cpp +6 -1
- data/src/agent/Core/CoreMain.cpp +26 -60
- data/src/agent/Core/SpawningKit/DirectSpawner.h +18 -6
- data/src/agent/Core/SpawningKit/ErrorRenderer.h +8 -8
- data/src/agent/Core/SpawningKit/Handshake/Perform.h +217 -61
- data/src/agent/Core/SpawningKit/Handshake/Prepare.h +57 -8
- data/src/agent/Core/SpawningKit/Handshake/Session.h +34 -1
- data/src/agent/Core/SpawningKit/Handshake/WorkDir.h +20 -4
- data/src/agent/Core/SpawningKit/SmartSpawner.h +90 -27
- data/src/agent/ExecHelper/ExecHelperMain.cpp +3 -0
- data/src/agent/Shared/ApiAccountUtils.h +2 -2
- data/src/agent/SpawnEnvSetupper/SpawnEnvSetupperMain.cpp +14 -4
- data/src/agent/Watchdog/Config.h +2 -1
- data/src/agent/Watchdog/WatchdogMain.cpp +38 -0
- data/src/apache2_module/Hooks.cpp +1 -0
- data/src/cxx_supportlib/ConfigKit/IN_PRACTICE.md +1 -1
- data/src/cxx_supportlib/ConfigKit/README.md +1 -1
- data/src/cxx_supportlib/Constants.h +6 -1
- data/src/cxx_supportlib/FileTools/FileManip.cpp +34 -2
- data/src/cxx_supportlib/FileTools/FileManip.h +58 -1
- data/src/cxx_supportlib/FileTools/PathManip.cpp +3 -2
- data/src/cxx_supportlib/FileTools/PathSecurityCheck.cpp +99 -0
- data/src/cxx_supportlib/FileTools/PathSecurityCheck.h +69 -0
- data/src/cxx_supportlib/Utils.cpp +37 -6
- data/src/cxx_supportlib/Utils.h +6 -0
- data/src/cxx_supportlib/Utils/AsyncSignalSafeUtils.h +14 -0
- data/src/cxx_supportlib/Utils/IOUtils.cpp +10 -18
- data/src/cxx_supportlib/Utils/IOUtils.h +10 -9
- data/src/cxx_supportlib/Utils/JsonUtils.h +12 -8
- data/src/cxx_supportlib/Utils/SystemMetricsCollector.h +4 -4
- data/src/cxx_supportlib/Utils/SystemTime.h +1 -1
- data/src/cxx_supportlib/WebSocketCommandReverseServer.h +3 -3
- data/src/cxx_supportlib/oxt/system_calls.cpp +25 -1
- data/src/cxx_supportlib/oxt/system_calls.hpp +3 -1
- data/src/helper-scripts/meteor-loader.rb +115 -28
- data/src/helper-scripts/rack-preloader.rb +1 -1
- data/src/nginx_module/ConfigGeneral/AutoGeneratedDefinitions.c +4 -4
- data/src/nginx_module/ConfigGeneral/AutoGeneratedSetterFuncs.c +4 -4
- data/src/nginx_module/LocationConfig/AutoGeneratedCreateFunction.c +0 -10
- data/src/nginx_module/LocationConfig/AutoGeneratedHeaderSerialization.c +0 -42
- data/src/nginx_module/LocationConfig/AutoGeneratedMergeFunction.c +0 -6
- data/src/nginx_module/LocationConfig/AutoGeneratedStruct.h +0 -8
- data/src/nginx_module/MainConfig/AutoGeneratedCreateFunction.c +10 -0
- data/src/nginx_module/MainConfig/AutoGeneratedManifestGeneration.c +22 -0
- data/src/nginx_module/MainConfig/AutoGeneratedStruct.h +8 -0
- data/src/nginx_module/ngx_http_passenger_module.c +6 -5
- data/src/ruby_supportlib/phusion_passenger.rb +1 -1
- data/src/ruby_supportlib/phusion_passenger/apache2/config_options.rb +0 -1
- data/src/ruby_supportlib/phusion_passenger/common_library.rb +3 -0
- data/src/ruby_supportlib/phusion_passenger/config/installation_utils.rb +3 -3
- data/src/ruby_supportlib/phusion_passenger/constants.rb +5 -0
- data/src/ruby_supportlib/phusion_passenger/nginx/config_options.rb +4 -2
- data/src/ruby_supportlib/phusion_passenger/platform_info.rb +3 -3
- data/src/ruby_supportlib/phusion_passenger/request_handler.rb +1 -1
- data/src/ruby_supportlib/phusion_passenger/vendor/daemon_controller.rb +1 -1
- metadata +4 -2
@@ -267,6 +267,10 @@
|
|
267
267
|
"read_only" : true,
|
268
268
|
"type" : "string"
|
269
269
|
},
|
270
|
+
"max_instances_per_app" : {
|
271
|
+
"read_only" : true,
|
272
|
+
"type" : "unsigned integer"
|
273
|
+
},
|
270
274
|
"min_spare_clients" : {
|
271
275
|
"default_value" : 0,
|
272
276
|
"has_default_value" : "static",
|
@@ -289,7 +293,7 @@
|
|
289
293
|
"type" : "unsigned integer"
|
290
294
|
},
|
291
295
|
"server_software" : {
|
292
|
-
"default_value" : "Phusion_Passenger/5.3.
|
296
|
+
"default_value" : "Phusion_Passenger/5.3.2",
|
293
297
|
"has_default_value" : "static",
|
294
298
|
"type" : "string"
|
295
299
|
},
|
@@ -724,6 +728,10 @@
|
|
724
728
|
"has_default_value" : "static",
|
725
729
|
"type" : "any"
|
726
730
|
},
|
731
|
+
"max_instances_per_app" : {
|
732
|
+
"read_only" : true,
|
733
|
+
"type" : "unsigned integer"
|
734
|
+
},
|
727
735
|
"max_pool_size" : {
|
728
736
|
"default_value" : 6,
|
729
737
|
"has_default_value" : "static",
|
@@ -787,7 +795,7 @@
|
|
787
795
|
"type" : "string"
|
788
796
|
},
|
789
797
|
"server_software" : {
|
790
|
-
"default_value" : "Phusion_Passenger/5.3.
|
798
|
+
"default_value" : "Phusion_Passenger/5.3.2",
|
791
799
|
"has_default_value" : "static",
|
792
800
|
"type" : "string"
|
793
801
|
},
|
@@ -1441,6 +1449,10 @@
|
|
1441
1449
|
"has_default_value" : "static",
|
1442
1450
|
"type" : "any"
|
1443
1451
|
},
|
1452
|
+
"max_instances_per_app" : {
|
1453
|
+
"read_only" : true,
|
1454
|
+
"type" : "unsigned integer"
|
1455
|
+
},
|
1444
1456
|
"max_pool_size" : {
|
1445
1457
|
"default_value" : 6,
|
1446
1458
|
"has_default_value" : "static",
|
@@ -1505,7 +1517,7 @@
|
|
1505
1517
|
"type" : "string"
|
1506
1518
|
},
|
1507
1519
|
"server_software" : {
|
1508
|
-
"default_value" : "Phusion_Passenger/5.3.
|
1520
|
+
"default_value" : "Phusion_Passenger/5.3.2",
|
1509
1521
|
"has_default_value" : "static",
|
1510
1522
|
"type" : "string"
|
1511
1523
|
},
|
@@ -35,6 +35,7 @@
|
|
35
35
|
#include <boost/bind.hpp>
|
36
36
|
#include <boost/foreach.hpp>
|
37
37
|
|
38
|
+
#include <limits>
|
38
39
|
#include <string>
|
39
40
|
#include <vector>
|
40
41
|
|
@@ -45,6 +46,7 @@
|
|
45
46
|
#include <Core/ApplicationPool/Pool.h>
|
46
47
|
#include <Core/Controller.h>
|
47
48
|
#include <ProcessManagement/Ruby.h>
|
49
|
+
#include <FileTools/FileManip.h>
|
48
50
|
#include <Utils.h>
|
49
51
|
#include <Utils/StrIntUtils.h>
|
50
52
|
#include <Utils/IOUtils.h>
|
@@ -486,7 +488,8 @@ private:
|
|
486
488
|
_exit(1);
|
487
489
|
} else {
|
488
490
|
pipe.second.close();
|
489
|
-
string out = readAll(pipe.first
|
491
|
+
string out = readAll(pipe.first,
|
492
|
+
std::numeric_limits<size_t>::max()).first;
|
490
493
|
LoggingKit::context->saveMonitoredFileLog(key, f.c_str(), f.size(),
|
491
494
|
out.data(), out.size());
|
492
495
|
pipe.first.close();
|
@@ -572,7 +575,7 @@ private:
|
|
572
575
|
Json::Value doc;
|
573
576
|
Json::Reader reader;
|
574
577
|
|
575
|
-
if (!reader.parse(
|
578
|
+
if (!reader.parse(unsafeReadFile(instanceDir + "/properties.json"), doc)) {
|
576
579
|
throw RuntimeException("Cannot parse " + instanceDir + "/properties.json: "
|
577
580
|
+ reader.getFormattedErrorMessages());
|
578
581
|
}
|
@@ -70,6 +70,8 @@ Group::processLowerLimitsSatisfied() const {
|
|
70
70
|
*/
|
71
71
|
bool
|
72
72
|
Group::processUpperLimitsReached() const {
|
73
|
+
// check maxInstances limit as set by Enterprise (OSS maxInstancesPerApp piggybacks on this,
|
74
|
+
// see InitRequest.cpp)
|
73
75
|
return options.maxProcesses != 0 && capacityUsed() >= options.maxProcesses;
|
74
76
|
}
|
75
77
|
|
data/src/agent/Core/Config.h
CHANGED
@@ -140,6 +140,7 @@ using namespace std;
|
|
140
140
|
* integration_mode string - default("standalone")
|
141
141
|
* log_level string - default("notice")
|
142
142
|
* log_target any - default({"stderr": true})
|
143
|
+
* max_instances_per_app unsigned integer - read_only
|
143
144
|
* max_pool_size unsigned integer - default(6)
|
144
145
|
* multi_app boolean - default(false),read_only
|
145
146
|
* passenger_root string required read_only
|
@@ -153,7 +154,7 @@ using namespace std;
|
|
153
154
|
* security_update_checker_interval unsigned integer - default(86400)
|
154
155
|
* security_update_checker_proxy_url string - -
|
155
156
|
* security_update_checker_url string - default("https://securitycheck.phusionpassenger.com/v1/check.json")
|
156
|
-
* server_software string - default("Phusion_Passenger/5.3.
|
157
|
+
* server_software string - default("Phusion_Passenger/5.3.2")
|
157
158
|
* show_version_in_header boolean - default(true)
|
158
159
|
* single_app_mode_app_root string - default,read_only
|
159
160
|
* single_app_mode_app_type string - read_only
|
@@ -107,11 +107,12 @@ parseControllerBenchmarkMode(const StaticString &mode) {
|
|
107
107
|
* default_user string - default("nobody")
|
108
108
|
* graceful_exit boolean - default(true)
|
109
109
|
* integration_mode string - default("standalone"),read_only
|
110
|
+
* max_instances_per_app unsigned integer - read_only
|
110
111
|
* min_spare_clients unsigned integer - default(0)
|
111
112
|
* multi_app boolean - default(true),read_only
|
112
113
|
* request_freelist_limit unsigned integer - default(1024)
|
113
114
|
* response_buffer_high_watermark unsigned integer - default(134217728)
|
114
|
-
* server_software string - default("Phusion_Passenger/5.3.
|
115
|
+
* server_software string - default("Phusion_Passenger/5.3.2")
|
115
116
|
* show_version_in_header boolean - default(true)
|
116
117
|
* start_reading_after_accept boolean - default(true)
|
117
118
|
* stat_throttle_rate unsigned integer - default(10)
|
@@ -127,6 +128,8 @@ private:
|
|
127
128
|
void initialize() {
|
128
129
|
using namespace ConfigKit;
|
129
130
|
|
131
|
+
add("max_instances_per_app", UINT_TYPE, OPTIONAL | READ_ONLY);
|
132
|
+
|
130
133
|
add("thread_number", UINT_TYPE, REQUIRED | READ_ONLY);
|
131
134
|
add("multi_app", BOOL_TYPE, OPTIONAL | READ_ONLY, true);
|
132
135
|
add("turbocaching", BOOL_TYPE, OPTIONAL | READ_ONLY, true);
|
@@ -307,6 +310,7 @@ public:
|
|
307
310
|
unsigned int responseBufferHighWatermark;
|
308
311
|
StaticString integrationMode;
|
309
312
|
StaticString serverLogName;
|
313
|
+
unsigned int maxInstancesPerApp;
|
310
314
|
ControllerBenchmarkMode benchmarkMode: 3;
|
311
315
|
bool singleAppMode: 1;
|
312
316
|
bool userSwitching: 1;
|
@@ -324,6 +328,7 @@ public:
|
|
324
328
|
responseBufferHighWatermark(config["response_buffer_high_watermark"].asUInt()),
|
325
329
|
integrationMode(psg_pstrdup(pool, config["integration_mode"].asString())),
|
326
330
|
serverLogName(createServerLogName()),
|
331
|
+
maxInstancesPerApp(config["max_instances_per_app"].asUInt()),
|
327
332
|
benchmarkMode(parseControllerBenchmarkMode(config["benchmark_mode"].asString())),
|
328
333
|
singleAppMode(!config["multi_app"].asBool()),
|
329
334
|
userSwitching(config["user_switching"].asBool()),
|
@@ -358,7 +358,6 @@ Controller::createNewPoolOptions(Client *client, Request *req,
|
|
358
358
|
fillPoolOption(req, options.user, "!~PASSENGER_USER");
|
359
359
|
fillPoolOption(req, options.group, "!~PASSENGER_GROUP");
|
360
360
|
fillPoolOption(req, options.minProcesses, "!~PASSENGER_MIN_PROCESSES");
|
361
|
-
fillPoolOption(req, options.maxProcesses, "!~PASSENGER_MAX_PROCESSES");
|
362
361
|
fillPoolOption(req, options.spawnMethod, "!~PASSENGER_SPAWN_METHOD");
|
363
362
|
fillPoolOption(req, options.startCommand, "!~PASSENGER_START_COMMAND");
|
364
363
|
fillPoolOptionSecToMsec(req, options.startTimeout, "!~PASSENGER_START_TIMEOUT");
|
@@ -372,6 +371,12 @@ Controller::createNewPoolOptions(Client *client, Request *req,
|
|
372
371
|
fillPoolOption(req, options.fileDescriptorUlimit, "!~PASSENGER_APP_FILE_DESCRIPTOR_ULIMIT");
|
373
372
|
fillPoolOption(req, options.raiseInternalError, "!~PASSENGER_RAISE_INTERNAL_ERROR");
|
374
373
|
fillPoolOption(req, options.lveMinUid, "!~PASSENGER_LVE_MIN_UID");
|
374
|
+
|
375
|
+
// maxProcesses is configured per-application by the (Enterprise) maxInstances option (and thus passed
|
376
|
+
// via request headers). In OSS the max processes can also be configured, but on a global level
|
377
|
+
// (i.e. the same for all apps) using the maxInstancesPerApp option. As an easy implementation shortcut
|
378
|
+
// we apply maxInstancesPerApp to options.maxProcesses (which can be overridden by Enterprise).
|
379
|
+
options.maxProcesses = mainConfig.maxInstancesPerApp;
|
375
380
|
/******************/
|
376
381
|
|
377
382
|
boost::shared_ptr<Options> optionsCopy = boost::make_shared<Options>(options);
|
data/src/agent/Core/CoreMain.cpp
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
-
* Copyright (c) 2010-
|
3
|
+
* Copyright (c) 2010-2018 Phusion Holding B.V.
|
4
4
|
*
|
5
5
|
* "Passenger", "Phusion Passenger" and "Union Station" are registered
|
6
6
|
* trademarks of Phusion Holding B.V.
|
@@ -38,6 +38,7 @@
|
|
38
38
|
|
39
39
|
#include <boost/config.hpp>
|
40
40
|
#include <boost/scoped_ptr.hpp>
|
41
|
+
#include <boost/foreach.hpp>
|
41
42
|
#include <sys/types.h>
|
42
43
|
#include <sys/socket.h>
|
43
44
|
#include <sys/stat.h>
|
@@ -86,10 +87,10 @@
|
|
86
87
|
#include <ResourceLocator.h>
|
87
88
|
#include <BackgroundEventLoop.cpp>
|
88
89
|
#include <FileTools/FileManip.h>
|
90
|
+
#include <FileTools/PathSecurityCheck.h>
|
89
91
|
#include <Exceptions.h>
|
90
92
|
#include <Utils.h>
|
91
93
|
#include <Utils/Timer.h>
|
92
|
-
#include <Utils/IOUtils.h>
|
93
94
|
#include <Utils/MessageIO.h>
|
94
95
|
#include <Utils/VariantMap.h>
|
95
96
|
#include <Core/OptionParser.h>
|
@@ -239,7 +240,7 @@ initializePrivilegedWorkingObjects() {
|
|
239
240
|
if (password.isString()) {
|
240
241
|
wo->controllerSecureHeadersPassword = password.asString();
|
241
242
|
} else if (password.isObject()) {
|
242
|
-
wo->controllerSecureHeadersPassword = strip(
|
243
|
+
wo->controllerSecureHeadersPassword = strip(unsafeReadFile(password["path"].asString()));
|
243
244
|
}
|
244
245
|
}
|
245
246
|
|
@@ -885,50 +886,6 @@ prestartWebApps() {
|
|
885
886
|
);
|
886
887
|
}
|
887
888
|
|
888
|
-
/**
|
889
|
-
* See warnIfPassengerRootVulnerable()
|
890
|
-
*/
|
891
|
-
static void
|
892
|
-
warnIfPathVulnerable(const char *path, string &warnings) {
|
893
|
-
struct stat pathStat;
|
894
|
-
|
895
|
-
if (stat(path, &pathStat) == -1) {
|
896
|
-
P_DEBUG("Vulnerability check skipped: stat error on " << path << " (errno: " << errno << ")");
|
897
|
-
return; // fatal: we need that stat for both checks below
|
898
|
-
}
|
899
|
-
|
900
|
-
// Non-root ownership
|
901
|
-
struct passwd pathOwner;
|
902
|
-
struct passwd *pwdResult;
|
903
|
-
|
904
|
-
boost::shared_array<char> strings;
|
905
|
-
long stringsBufSize = std::max<long>(1024 * 128, sysconf(_SC_GETPW_R_SIZE_MAX));
|
906
|
-
strings.reset(new char[stringsBufSize]);
|
907
|
-
errno = 0;
|
908
|
-
if (getpwuid_r(pathStat.st_uid, &pathOwner, strings.get(), stringsBufSize, &pwdResult) == -1) {
|
909
|
-
P_DEBUG("Vulnerability check (owner) skipped: getpwuid_r error on " << path << " (owner UID: " <<
|
910
|
-
pathStat.st_uid << ", errno: " << errno << ")");
|
911
|
-
} else if (pwdResult == NULL) {
|
912
|
-
P_DEBUG("Vulnerability check (owner) skipped: getpwuid_r empty on " << path << " (owner UID: " <<
|
913
|
-
pathStat.st_uid << ", errno: " << errno << ")");
|
914
|
-
} else if (pathOwner.pw_uid != 0) {
|
915
|
-
warnings.append("\nThe path \"");
|
916
|
-
warnings.append(path);
|
917
|
-
warnings.append("\" can be modified by user \"");
|
918
|
-
warnings.append(pathOwner.pw_name);
|
919
|
-
warnings.append("\" (or applications running as that user)."
|
920
|
-
" Change the owner of the path to root, or avoid running " SHORT_PROGRAM_NAME " as root.");
|
921
|
-
}
|
922
|
-
|
923
|
-
// World writeable access rights
|
924
|
-
if ((pathStat.st_mode & S_IWOTH) != 0) {
|
925
|
-
warnings.append("\nThe path \"");
|
926
|
-
warnings.append(path);
|
927
|
-
warnings.append("\" is writeable by any user (or application)."
|
928
|
-
" Limit write access on the path to only the root user/group.");
|
929
|
-
}
|
930
|
-
}
|
931
|
-
|
932
889
|
/*
|
933
890
|
* Emit a warning (log) if the Passenger root dir (and/or its parents) can be modified by non-root users
|
934
891
|
* while Passenger was run as root (because non-root users can then tamper with something running as root).
|
@@ -948,19 +905,28 @@ warnIfPassengerRootVulnerable() {
|
|
948
905
|
}
|
949
906
|
|
950
907
|
string root = workingObjects->resourceLocator.getInstallSpec();
|
951
|
-
string
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
908
|
+
vector<string> errors, checkErrors;
|
909
|
+
if (isPathProbablySecureForRootUse(root, errors, checkErrors)) {
|
910
|
+
if (!checkErrors.empty()) {
|
911
|
+
string message = "WARNING: unable to perform privilege escalation vulnerability detection:\n";
|
912
|
+
foreach (string line, checkErrors) {
|
913
|
+
message.append("\n - " + line);
|
914
|
+
}
|
915
|
+
P_WARN(message);
|
916
|
+
}
|
917
|
+
} else {
|
918
|
+
string message = "WARNING: potential privilege escalation vulnerability detected. " \
|
919
|
+
PROGRAM_NAME " is running as root, and part(s) of the " SHORT_PROGRAM_NAME
|
920
|
+
" root path (" + root + ") can be changed by non-root user(s):\n";
|
921
|
+
foreach (string line, errors) {
|
922
|
+
message.append("\n - " + line);
|
923
|
+
}
|
924
|
+
foreach (string line, checkErrors) {
|
925
|
+
message.append("\n - " + line);
|
926
|
+
}
|
927
|
+
message.append("\n\nPlease either fix up the permissions for the insecure paths, or install "
|
928
|
+
SHORT_PROGRAM_NAME " in a different location that can only be modified by root.");
|
929
|
+
P_WARN(message);
|
964
930
|
}
|
965
931
|
}
|
966
932
|
|
@@ -37,9 +37,11 @@
|
|
37
37
|
#include <LoggingKit/LoggingKit.h>
|
38
38
|
#include <LveLoggingDecorator.h>
|
39
39
|
#include <Utils/IOUtils.h>
|
40
|
+
#include <Utils/AsyncSignalSafeUtils.h>
|
40
41
|
|
41
42
|
#include <limits.h> // for PTHREAD_STACK_MIN
|
42
43
|
#include <pthread.h>
|
44
|
+
#include <unistd.h>
|
43
45
|
#include <adhoc_lve.h>
|
44
46
|
|
45
47
|
namespace Passenger {
|
@@ -150,8 +152,11 @@ private:
|
|
150
152
|
|
151
153
|
pid_t pid = syscalls::fork();
|
152
154
|
if (pid == 0) {
|
153
|
-
|
154
|
-
|
155
|
+
int e;
|
156
|
+
char buf[1024];
|
157
|
+
const char *end = buf + sizeof(buf);
|
158
|
+
namespace ASSU = AsyncSignalSafeUtils;
|
159
|
+
|
155
160
|
resetSignalHandlersAndMask();
|
156
161
|
disableMallocDebugging();
|
157
162
|
int stdinCopy = dup2(stdinChannel.first, 3);
|
@@ -160,6 +165,7 @@ private:
|
|
160
165
|
dup2(stdoutAndErrCopy, 1);
|
161
166
|
dup2(stdoutAndErrCopy, 2);
|
162
167
|
closeAllFileDescriptors(2, true);
|
168
|
+
|
163
169
|
execlp(agentFilename.c_str(),
|
164
170
|
agentFilename.c_str(),
|
165
171
|
"spawn-env-setupper",
|
@@ -167,10 +173,16 @@ private:
|
|
167
173
|
"--before",
|
168
174
|
NULL);
|
169
175
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
176
|
+
char *pos = buf;
|
177
|
+
e = errno;
|
178
|
+
pos = ASSU::appendData(pos, end, "Cannot execute \"");
|
179
|
+
pos = ASSU::appendData(pos, end, agentFilename.data(), agentFilename.size());
|
180
|
+
pos = ASSU::appendData(pos, end, "\": ");
|
181
|
+
pos = ASSU::appendData(pos, end, ASSU::limitedStrerror(e));
|
182
|
+
pos = ASSU::appendData(pos, end, " (errno=");
|
183
|
+
pos = ASSU::appendInteger<int, 10>(pos, end, e);
|
184
|
+
pos = ASSU::appendData(pos, end, ")\n");
|
185
|
+
ASSU::printError(buf, pos - buf);
|
174
186
|
_exit(1);
|
175
187
|
|
176
188
|
} else if (pid == -1) {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
-
* Copyright (c) 2014-
|
3
|
+
* Copyright (c) 2014-2018 Phusion Holding B.V.
|
4
4
|
*
|
5
5
|
* "Passenger", "Phusion Passenger" and "Union Station" are registered
|
6
6
|
* trademarks of Phusion Holding B.V.
|
@@ -34,8 +34,8 @@
|
|
34
34
|
|
35
35
|
#include <Constants.h>
|
36
36
|
#include <StaticString.h>
|
37
|
+
#include <FileTools/FileManip.h>
|
37
38
|
#include <Utils/Template.h>
|
38
|
-
#include <Utils/IOUtils.h>
|
39
39
|
#include <Core/SpawningKit/Context.h>
|
40
40
|
#include <Core/SpawningKit/Exceptions.h>
|
41
41
|
|
@@ -61,8 +61,8 @@ public:
|
|
61
61
|
string htmlFile = templatesDir + "/with_details/src/index.html.template";
|
62
62
|
string cssFile = templatesDir + "/with_details/dist/styles.css";
|
63
63
|
string jsFile = templatesDir + "/with_details/dist/bundle.js";
|
64
|
-
string cssContent =
|
65
|
-
string jsContent =
|
64
|
+
string cssContent = unsafeReadFile(cssFile);
|
65
|
+
string jsContent = unsafeReadFile(jsFile);
|
66
66
|
|
67
67
|
Json::Value spec;
|
68
68
|
spec["program_name"] = PROGRAM_NAME;
|
@@ -85,7 +85,7 @@ public:
|
|
85
85
|
params.set("TITLE", "Web application could not be started");
|
86
86
|
params.set("SPEC", specContent);
|
87
87
|
|
88
|
-
return Template::apply(
|
88
|
+
return Template::apply(unsafeReadFile(htmlFile), params);
|
89
89
|
}
|
90
90
|
|
91
91
|
string renderWithoutDetails(const SpawningKit::SpawnException &e) const {
|
@@ -93,8 +93,8 @@ public:
|
|
93
93
|
string htmlFile = templatesDir + "/without_details/src/index.html.template";
|
94
94
|
string cssFile = templatesDir + "/without_details/dist/styles.css";
|
95
95
|
string jsFile = templatesDir + "/without_details/dist/bundle.js";
|
96
|
-
string cssContent =
|
97
|
-
string jsContent =
|
96
|
+
string cssContent = unsafeReadFile(cssFile);
|
97
|
+
string jsContent = unsafeReadFile(jsFile);
|
98
98
|
|
99
99
|
params.set("CSS", cssContent);
|
100
100
|
params.set("JS", jsContent);
|
@@ -106,7 +106,7 @@ public:
|
|
106
106
|
params.set("PROGRAM_WEBSITE", PROGRAM_WEBSITE);
|
107
107
|
params.set("PROGRAM_AUTHOR", PROGRAM_AUTHOR);
|
108
108
|
|
109
|
-
return Template::apply(
|
109
|
+
return Template::apply(unsafeReadFile(htmlFile), params);
|
110
110
|
}
|
111
111
|
};
|
112
112
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
-
* Copyright (c) 2016-
|
3
|
+
* Copyright (c) 2016-2018 Phusion Holding B.V.
|
4
4
|
*
|
5
5
|
* "Passenger", "Phusion Passenger" and "Union Station" are registered
|
6
6
|
* trademarks of Phusion Holding B.V.
|
@@ -48,6 +48,8 @@
|
|
48
48
|
#include <Constants.h>
|
49
49
|
#include <Exceptions.h>
|
50
50
|
#include <FileDescriptor.h>
|
51
|
+
#include <FileTools/FileManip.h>
|
52
|
+
#include <FileTools/PathManip.h>
|
51
53
|
#include <Utils.h>
|
52
54
|
#include <Utils/ScopeGuard.h>
|
53
55
|
#include <Utils/SystemTime.h>
|
@@ -146,7 +148,8 @@ private:
|
|
146
148
|
TRACE_POINT();
|
147
149
|
try {
|
148
150
|
string path = session.responseDir + "/finish";
|
149
|
-
int fd = syscalls::
|
151
|
+
int fd = syscalls::openat(session.responseDirFd, "finish",
|
152
|
+
O_RDONLY | O_NOFOLLOW);
|
150
153
|
if (fd == -1) {
|
151
154
|
int e = errno;
|
152
155
|
throw FileSystemException("Error opening FIFO " + path,
|
@@ -370,6 +373,7 @@ private:
|
|
370
373
|
}
|
371
374
|
|
372
375
|
void loadResultPropertiesFromResponseDir(bool socketsRequired) {
|
376
|
+
TRACE_POINT();
|
373
377
|
Result &result = session.result;
|
374
378
|
string path = session.responseDir + "/properties.json";
|
375
379
|
Json::Reader reader;
|
@@ -377,19 +381,29 @@ private:
|
|
377
381
|
vector<string> errors;
|
378
382
|
|
379
383
|
// We already checked whether properties.json exists before invoking
|
380
|
-
// this method, so if
|
384
|
+
// this method, so if safeReadFile() fails then we can't be sure that
|
381
385
|
// it's an application problem. This is why we want the SystemException
|
382
386
|
// to propagate to higher layers so that there it can be turned into
|
383
387
|
// a generic filesystem-related or IO-related SpawnException, as opposed
|
384
388
|
// to one about this problem specifically.
|
385
389
|
|
386
|
-
|
390
|
+
UPDATE_TRACE_POINT();
|
391
|
+
pair<string, bool> jsonContent = safeReadFile(session.responseDirFd, "properties.json",
|
392
|
+
SPAWNINGKIT_MAX_PROPERTIES_JSON_SIZE);
|
393
|
+
if (!jsonContent.second) {
|
394
|
+
errors.push_back("Error parsing " + path + ": file bigger than "
|
395
|
+
+ toString(SPAWNINGKIT_MAX_PROPERTIES_JSON_SIZE) + " bytes");
|
396
|
+
throwSpawnExceptionBecauseOfResultValidationErrors(vector<string>(),
|
397
|
+
errors);
|
398
|
+
}
|
399
|
+
if (!reader.parse(jsonContent.first, doc)) {
|
387
400
|
errors.push_back("Error parsing " + path + ": " +
|
388
401
|
reader.getFormattedErrorMessages());
|
389
402
|
throwSpawnExceptionBecauseOfResultValidationErrors(vector<string>(),
|
390
403
|
errors);
|
391
404
|
}
|
392
405
|
|
406
|
+
UPDATE_TRACE_POINT();
|
393
407
|
validateResultPropertiesFile(doc, socketsRequired, errors);
|
394
408
|
if (!errors.empty()) {
|
395
409
|
errors.insert(errors.begin(), "The following errors were detected in "
|
@@ -402,6 +416,7 @@ private:
|
|
402
416
|
return;
|
403
417
|
}
|
404
418
|
|
419
|
+
UPDATE_TRACE_POINT();
|
405
420
|
Json::Value::iterator it, end = doc["sockets"].end();
|
406
421
|
for (it = doc["sockets"].begin(); it != end; it++) {
|
407
422
|
const Json::Value &socketDoc = *it;
|
@@ -423,44 +438,50 @@ private:
|
|
423
438
|
void validateResultPropertiesFile(const Json::Value &doc, bool socketsRequired,
|
424
439
|
vector<string> &errors) const
|
425
440
|
{
|
441
|
+
TRACE_POINT();
|
426
442
|
if (!doc.isMember("sockets")) {
|
427
443
|
if (socketsRequired) {
|
428
444
|
errors.push_back("'sockets' must be specified");
|
429
445
|
}
|
430
|
-
|
446
|
+
return;
|
447
|
+
}
|
448
|
+
if (!doc["sockets"].isArray()) {
|
431
449
|
errors.push_back("'sockets' must be an array");
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
Json::Value::const_iterator it, end = doc["sockets"].end();
|
439
|
-
for (it = doc["sockets"].begin(); it != end; it++) {
|
440
|
-
const Json::Value &socketDoc = *it;
|
450
|
+
return;
|
451
|
+
}
|
452
|
+
if (socketsRequired && doc["sockets"].empty()) {
|
453
|
+
errors.push_back("'sockets' must be non-empty");
|
454
|
+
return;
|
455
|
+
}
|
441
456
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
}
|
457
|
+
UPDATE_TRACE_POINT();
|
458
|
+
Json::Value::const_iterator it, end = doc["sockets"].end();
|
459
|
+
for (it = doc["sockets"].begin(); it != end; it++) {
|
460
|
+
const Json::Value &socketDoc = *it;
|
447
461
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
"protocol", Json::stringValue, it.index(),
|
453
|
-
true, true, errors);
|
454
|
-
validateResultPropertiesFileSocketField(socketDoc,
|
455
|
-
"description", Json::stringValue, it.index(),
|
456
|
-
false, true, errors);
|
457
|
-
validateResultPropertiesFileSocketField(socketDoc,
|
458
|
-
"concurrency", Json::intValue, it.index(),
|
459
|
-
true, false, errors);
|
460
|
-
validateResultPropertiesFileSocketField(socketDoc,
|
461
|
-
"accept_http_requests", Json::booleanValue, it.index(),
|
462
|
-
false, false, errors);
|
462
|
+
if (!socketDoc.isObject()) {
|
463
|
+
errors.push_back("'sockets[" + toString(it.index())
|
464
|
+
+ "]' must be an object");
|
465
|
+
continue;
|
463
466
|
}
|
467
|
+
|
468
|
+
validateResultPropertiesFileSocketField(socketDoc,
|
469
|
+
"address", Json::stringValue, it.index(),
|
470
|
+
true, true, errors);
|
471
|
+
validateResultPropertiesFileSocketField(socketDoc,
|
472
|
+
"protocol", Json::stringValue, it.index(),
|
473
|
+
true, true, errors);
|
474
|
+
validateResultPropertiesFileSocketField(socketDoc,
|
475
|
+
"description", Json::stringValue, it.index(),
|
476
|
+
false, true, errors);
|
477
|
+
validateResultPropertiesFileSocketField(socketDoc,
|
478
|
+
"concurrency", Json::intValue, it.index(),
|
479
|
+
true, false, errors);
|
480
|
+
validateResultPropertiesFileSocketField(socketDoc,
|
481
|
+
"accept_http_requests", Json::booleanValue, it.index(),
|
482
|
+
false, false, errors);
|
483
|
+
validateResultPropertiesFileSocketAddress(socketDoc,
|
484
|
+
it.index(), errors);
|
464
485
|
}
|
465
486
|
}
|
466
487
|
|
@@ -497,6 +518,93 @@ private:
|
|
497
518
|
}
|
498
519
|
}
|
499
520
|
|
521
|
+
void validateResultPropertiesFileSocketAddress(const Json::Value &doc,
|
522
|
+
unsigned int index, vector<string> &errors) const
|
523
|
+
{
|
524
|
+
TRACE_POINT();
|
525
|
+
if (!doc["address"].isString()
|
526
|
+
|| getSocketAddressType(doc["address"].asString()) != SAT_UNIX)
|
527
|
+
{
|
528
|
+
return;
|
529
|
+
}
|
530
|
+
|
531
|
+
string filename = parseUnixSocketAddress(doc["address"].asString());
|
532
|
+
|
533
|
+
if (filename.empty()) {
|
534
|
+
errors.push_back("'sockets[" + toString(index)
|
535
|
+
+ "].address' contains an empty Unix domain socket filename");
|
536
|
+
return;
|
537
|
+
}
|
538
|
+
|
539
|
+
if (filename[0] != '/') {
|
540
|
+
errors.push_back("'sockets[" + toString(index)
|
541
|
+
+ "].address' when referring to a Unix domain socket, must be"
|
542
|
+
" an absolute path (given path: " + filename + ")");
|
543
|
+
return;
|
544
|
+
}
|
545
|
+
|
546
|
+
// If any of the parent directories is writable by a normal user
|
547
|
+
// (Joe) that is not the app's user (Jane), then Joe can swap that
|
548
|
+
// directory with something else, with contents controlled by Joe.
|
549
|
+
// That way, Joe can cause Passenger to connect to (and forward
|
550
|
+
// Jane's traffic to) a process that does not actually belong to
|
551
|
+
// Jane.
|
552
|
+
//
|
553
|
+
// To mitigate this risk, we insist that the socket be placed in a
|
554
|
+
// directory that we know is safe (instanceDir + "/apps.s").
|
555
|
+
// We don't rely on isPathProbablySecureForRootUse() because that
|
556
|
+
// function cannot be 100% sure that it is correct.
|
557
|
+
|
558
|
+
UPDATE_TRACE_POINT();
|
559
|
+
// instanceDir is only empty in tests
|
560
|
+
if (!session.context->instanceDir.empty()) {
|
561
|
+
StaticString actualDir = extractDirNameStatic(filename);
|
562
|
+
string expectedDir = session.context->instanceDir + "/apps.s";
|
563
|
+
if (actualDir != expectedDir) {
|
564
|
+
errors.push_back("'sockets[" + toString(index)
|
565
|
+
+ "].address', when referring to a Unix domain socket,"
|
566
|
+
" must be an absolute path to a file in '"
|
567
|
+
+ expectedDir + "' (given path: " + filename + ")");
|
568
|
+
return;
|
569
|
+
}
|
570
|
+
}
|
571
|
+
|
572
|
+
UPDATE_TRACE_POINT();
|
573
|
+
struct stat s;
|
574
|
+
int ret;
|
575
|
+
do {
|
576
|
+
ret = lstat(filename.c_str(), &s);
|
577
|
+
} while (ret == -1 && errno == EAGAIN);
|
578
|
+
|
579
|
+
if (ret == -1) {
|
580
|
+
int e = errno;
|
581
|
+
if (e == EEXIST) {
|
582
|
+
errors.push_back("'sockets[" + toString(index)
|
583
|
+
+ "].address' refers to a non-existant Unix domain"
|
584
|
+
" socket file (given path: " + filename + ")");
|
585
|
+
return;
|
586
|
+
} else {
|
587
|
+
throw FileSystemException("Cannot stat " + filename,
|
588
|
+
e, filename);
|
589
|
+
}
|
590
|
+
}
|
591
|
+
|
592
|
+
// We only check the UID, not the GID, because the socket
|
593
|
+
// may be automatically made with a different GID than
|
594
|
+
// the creating process's due to the setgid bit being set
|
595
|
+
// the directory that contains the socket. Furthermore,
|
596
|
+
// on macOS it seems that all directories behave as if
|
597
|
+
// they have the setgid bit set.
|
598
|
+
|
599
|
+
UPDATE_TRACE_POINT();
|
600
|
+
if (s.st_uid != session.uid) {
|
601
|
+
errors.push_back("'sockets[" + toString(index)
|
602
|
+
+ "].address', when referring to a Unix domain socket file,"
|
603
|
+
" must be owned by user " + getUserName(session.uid)
|
604
|
+
+ " (actual owner: " + getUserName(s.st_uid) + ")");
|
605
|
+
}
|
606
|
+
}
|
607
|
+
|
500
608
|
bool resultHasSocketWithPreloaderProtocol() const {
|
501
609
|
const vector<Result::Socket> &sockets = session.result.sockets;
|
502
610
|
vector<Result::Socket>::const_iterator it, end = sockets.end();
|
@@ -553,6 +661,7 @@ private:
|
|
553
661
|
SpawnException e(INTERNAL_ERROR, session.journey, config);
|
554
662
|
e.setSubprocessPid(pid);
|
555
663
|
e.setStdoutAndErrData(getStdoutErrData());
|
664
|
+
loadBasicInfoFromEnvDumpDir(e);
|
556
665
|
loadAnnotationsFromEnvDumpDir(e);
|
557
666
|
|
558
667
|
if (config->wrapperSuppliedByThirdParty) {
|
@@ -614,6 +723,7 @@ private:
|
|
614
723
|
SpawnException e(INTERNAL_ERROR, session.journey, config);
|
615
724
|
e.setSubprocessPid(pid);
|
616
725
|
e.setStdoutAndErrData(getStdoutErrData());
|
726
|
+
loadBasicInfoFromEnvDumpDir(e);
|
617
727
|
loadAnnotationsFromEnvDumpDir(e);
|
618
728
|
|
619
729
|
e.setSummary("Error spawning the web application: the application"
|
@@ -660,6 +770,7 @@ private:
|
|
660
770
|
SpawnException e(INTERNAL_ERROR, session.journey, config);
|
661
771
|
e.setSubprocessPid(pid);
|
662
772
|
e.setStdoutAndErrData(getStdoutErrData());
|
773
|
+
loadBasicInfoFromEnvDumpDir(e);
|
663
774
|
loadAnnotationsFromEnvDumpDir(e);
|
664
775
|
|
665
776
|
if (config->wrapperSuppliedByThirdParty) {
|
@@ -731,6 +842,7 @@ private:
|
|
731
842
|
SpawnException e(INTERNAL_ERROR, session.journey, config);
|
732
843
|
e.setSubprocessPid(pid);
|
733
844
|
e.setStdoutAndErrData(getStdoutErrData());
|
845
|
+
loadBasicInfoFromEnvDumpDir(e);
|
734
846
|
loadAnnotationsFromEnvDumpDir(e);
|
735
847
|
|
736
848
|
e.setSummary("Error spawning the web application: the application"
|
@@ -773,6 +885,8 @@ private:
|
|
773
885
|
e.setSubprocessPid(pid);
|
774
886
|
e.setStdoutAndErrData(getStdoutErrData());
|
775
887
|
e.setAdvancedProblemDetails(toString(internalFieldErrors));
|
888
|
+
loadBasicInfoFromEnvDumpDir(e);
|
889
|
+
loadAnnotationsFromEnvDumpDir(e);
|
776
890
|
|
777
891
|
e.setSummary("Error spawning the web application:"
|
778
892
|
" a bug in " SHORT_PROGRAM_NAME " caused the"
|
@@ -802,12 +916,23 @@ private:
|
|
802
916
|
} else if (!config->genericApp && config->startsUsingWrapper) {
|
803
917
|
UPDATE_TRACE_POINT();
|
804
918
|
loadJourneyStateFromResponseDir();
|
805
|
-
session.journey.
|
919
|
+
switch (session.journey.getType()) {
|
920
|
+
case SPAWN_DIRECTLY:
|
921
|
+
case START_PRELOADER:
|
922
|
+
session.journey.setStepErrored(SUBPROCESS_WRAPPER_PREPARATION, true);
|
923
|
+
break;
|
924
|
+
case SPAWN_THROUGH_PRELOADER:
|
925
|
+
session.journey.setStepErrored(SUBPROCESS_PREPARE_AFTER_FORKING_FROM_PRELOADER, true);
|
926
|
+
break;
|
927
|
+
default:
|
928
|
+
P_BUG("Unknown journey type " << (int) session.journey.getType());
|
929
|
+
}
|
806
930
|
|
807
931
|
SpawnException e(INTERNAL_ERROR, session.journey, config);
|
808
932
|
e.setSubprocessPid(pid);
|
809
933
|
e.setStdoutAndErrData(getStdoutErrData());
|
810
934
|
e.setAdvancedProblemDetails(toString(appSuppliedFieldErrors));
|
935
|
+
loadBasicInfoFromEnvDumpDir(e);
|
811
936
|
loadAnnotationsFromEnvDumpDir(e);
|
812
937
|
|
813
938
|
if (config->wrapperSuppliedByThirdParty) {
|
@@ -883,6 +1008,7 @@ private:
|
|
883
1008
|
e.setAdvancedProblemDetails(toString(appSuppliedFieldErrors));
|
884
1009
|
e.setSubprocessPid(pid);
|
885
1010
|
e.setStdoutAndErrData(getStdoutErrData());
|
1011
|
+
loadBasicInfoFromEnvDumpDir(e);
|
886
1012
|
loadAnnotationsFromEnvDumpDir(e);
|
887
1013
|
|
888
1014
|
message = "<p>The " PROGRAM_NAME " application server tried"
|
@@ -922,13 +1048,15 @@ private:
|
|
922
1048
|
ErrorCategory inferErrorCategoryFromResponseDir(ErrorCategory defaultValue) const {
|
923
1049
|
TRACE_POINT();
|
924
1050
|
if (fileExists(session.responseDir + "/error/category")) {
|
925
|
-
string value = strip(
|
1051
|
+
string value = strip(safeReadFile(session.responseErrorDirFd,
|
1052
|
+
"category", SPAWNINGKIT_MAX_ERROR_CATEGORY_SIZE).first);
|
926
1053
|
ErrorCategory category = stringToErrorCategory(value);
|
927
1054
|
|
928
1055
|
if (category == UNKNOWN_ERROR_CATEGORY) {
|
929
1056
|
SpawnException e(INTERNAL_ERROR, session.journey, config);
|
930
1057
|
e.setStdoutAndErrData(getStdoutErrData());
|
931
1058
|
e.setSubprocessPid(pid);
|
1059
|
+
loadBasicInfoFromEnvDumpDir(e);
|
932
1060
|
loadAnnotationsFromEnvDumpDir(e);
|
933
1061
|
|
934
1062
|
if (!config->genericApp && config->startsUsingWrapper) {
|
@@ -1047,18 +1175,24 @@ private:
|
|
1047
1175
|
continue;
|
1048
1176
|
}
|
1049
1177
|
|
1178
|
+
map<JourneyStep, int>::const_iterator it = session.stepDirFds.find(step);
|
1179
|
+
if (it == session.stepDirFds.end()) {
|
1180
|
+
P_BUG("No fd opened for step " << stepString);
|
1181
|
+
}
|
1182
|
+
|
1050
1183
|
loadJourneyStateFromResponseDirForSpecificStep(
|
1051
|
-
session, pid, stdoutAndErrCapturer, step, stepDir);
|
1184
|
+
session, pid, stdoutAndErrCapturer, step, stepDir, it->second);
|
1052
1185
|
}
|
1053
1186
|
}
|
1054
1187
|
|
1055
1188
|
static void loadJourneyStateFromResponseDirForSpecificStep(HandshakeSession &session,
|
1056
1189
|
pid_t pid, const BackgroundIOCapturerPtr &stdoutAndErrCapturer,
|
1057
|
-
JourneyStep step, const string &stepDir)
|
1190
|
+
JourneyStep step, const string &stepDir, int stepDirFd)
|
1058
1191
|
{
|
1059
1192
|
TRACE_POINT_WITH_DATA(journeyStepToString(step).data());
|
1060
1193
|
string summary;
|
1061
|
-
string value = strip(
|
1194
|
+
string value = strip(safeReadFile(stepDirFd, "state",
|
1195
|
+
SPAWNINGKIT_MAX_JOURNEY_STEP_FILE_SIZE).first);
|
1062
1196
|
JourneyStepState state = stringToJourneyStepState(value);
|
1063
1197
|
const Config *config = session.config;
|
1064
1198
|
|
@@ -1095,6 +1229,7 @@ private:
|
|
1095
1229
|
SpawnException e(INTERNAL_ERROR, session.journey, config);
|
1096
1230
|
e.setStdoutAndErrData(getStdoutErrData(stdoutAndErrCapturer));
|
1097
1231
|
e.setSubprocessPid(pid);
|
1232
|
+
loadBasicInfoFromEnvDumpDir(e, session);
|
1098
1233
|
loadAnnotationsFromEnvDumpDir(e, session);
|
1099
1234
|
|
1100
1235
|
if (!config->genericApp && config->startsUsingWrapper) {
|
@@ -1187,6 +1322,7 @@ private:
|
|
1187
1322
|
SpawnException e(INTERNAL_ERROR, session.journey, config);
|
1188
1323
|
e.setStdoutAndErrData(getStdoutErrData(stdoutAndErrCapturer));
|
1189
1324
|
e.setSubprocessPid(pid);
|
1325
|
+
loadBasicInfoFromEnvDumpDir(e, session);
|
1190
1326
|
loadAnnotationsFromEnvDumpDir(e, session);
|
1191
1327
|
|
1192
1328
|
if (!config->genericApp && config->startsUsingWrapper) {
|
@@ -1274,13 +1410,15 @@ private:
|
|
1274
1410
|
|
1275
1411
|
UPDATE_TRACE_POINT();
|
1276
1412
|
if (fileExists(stepDir + "/begin_time_monotonic")) {
|
1277
|
-
value =
|
1413
|
+
value = safeReadFile(stepDirFd, "begin_time_monotonic",
|
1414
|
+
SPAWNINGKIT_MAX_JOURNEY_STEP_FILE_SIZE).first;
|
1278
1415
|
MonotonicTimeUsec beginTimeMonotonic = atof(value.c_str()) * 1000000;
|
1279
1416
|
P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
|
1280
1417
|
<< ": monotonic begin time is \"" << cEscapeString(value) << "\"");
|
1281
1418
|
session.journey.setStepBeginTime(step, beginTimeMonotonic);
|
1282
1419
|
} else if (fileExists(stepDir + "/begin_time")) {
|
1283
|
-
value =
|
1420
|
+
value = safeReadFile(stepDirFd, "begin_time",
|
1421
|
+
SPAWNINGKIT_MAX_JOURNEY_STEP_FILE_SIZE).first;
|
1284
1422
|
unsigned long long beginTime = atof(value.c_str()) * 1000000;
|
1285
1423
|
MonotonicTimeUsec beginTimeMonotonic = usecTimestampToMonoTime(beginTime);
|
1286
1424
|
P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
|
@@ -1294,13 +1432,15 @@ private:
|
|
1294
1432
|
|
1295
1433
|
UPDATE_TRACE_POINT();
|
1296
1434
|
if (fileExists(stepDir + "/end_time_monotonic")) {
|
1297
|
-
value =
|
1435
|
+
value = safeReadFile(stepDirFd, "end_time_monotonic",
|
1436
|
+
SPAWNINGKIT_MAX_JOURNEY_STEP_FILE_SIZE).first;
|
1298
1437
|
MonotonicTimeUsec endTimeMonotonic = atof(value.c_str()) * 1000000;
|
1299
1438
|
P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
|
1300
1439
|
<< ": monotonic end time is \"" << cEscapeString(value) << "\"");
|
1301
1440
|
session.journey.setStepEndTime(step, endTimeMonotonic);
|
1302
1441
|
} else if (fileExists(stepDir + "/end_time")) {
|
1303
|
-
value =
|
1442
|
+
value = safeReadFile(stepDirFd, "end_time",
|
1443
|
+
SPAWNINGKIT_MAX_JOURNEY_STEP_FILE_SIZE).first;
|
1304
1444
|
unsigned long long endTime = atof(value.c_str()) * 1000000;
|
1305
1445
|
MonotonicTimeUsec endTimeMonotonic = usecTimestampToMonoTime(endTime);
|
1306
1446
|
P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
|
@@ -1330,40 +1470,51 @@ private:
|
|
1330
1470
|
void loadSubprocessErrorMessagesAndEnvDump(SpawnException &e) const {
|
1331
1471
|
TRACE_POINT();
|
1332
1472
|
const string &responseDir = session.responseDir;
|
1333
|
-
const string &envDumpDir = session.envDumpDir;
|
1334
1473
|
|
1335
1474
|
if (fileExists(responseDir + "/error/summary")) {
|
1336
|
-
e.setSummary(strip(
|
1475
|
+
e.setSummary(strip(safeReadFile(session.responseErrorDirFd, "summary",
|
1476
|
+
SPAWNINGKIT_MAX_SUBPROCESS_ERROR_MESSAGE_SIZE).first));
|
1337
1477
|
}
|
1338
1478
|
|
1339
1479
|
if (e.getAdvancedProblemDetails().empty()
|
1340
1480
|
&& fileExists(responseDir + "/error/advanced_problem_details"))
|
1341
1481
|
{
|
1342
|
-
e.setAdvancedProblemDetails(strip(
|
1343
|
-
|
1482
|
+
e.setAdvancedProblemDetails(strip(safeReadFile(session.responseErrorDirFd,
|
1483
|
+
"advanced_problem_details", SPAWNINGKIT_MAX_SUBPROCESS_ERROR_MESSAGE_SIZE).first));
|
1344
1484
|
}
|
1345
1485
|
|
1346
1486
|
if (fileExists(responseDir + "/error/problem_description.html")) {
|
1347
|
-
e.setProblemDescriptionHTML(
|
1487
|
+
e.setProblemDescriptionHTML(safeReadFile(session.responseErrorDirFd,
|
1488
|
+
"problem_description.html", SPAWNINGKIT_MAX_SUBPROCESS_ERROR_MESSAGE_SIZE).first);
|
1348
1489
|
} else if (fileExists(responseDir + "/error/problem_description.txt")) {
|
1349
|
-
e.setProblemDescriptionHTML(escapeHTML(strip(
|
1350
|
-
|
1490
|
+
e.setProblemDescriptionHTML(escapeHTML(strip(safeReadFile(session.responseErrorDirFd,
|
1491
|
+
"problem_description.txt", SPAWNINGKIT_MAX_SUBPROCESS_ERROR_MESSAGE_SIZE).first)));
|
1351
1492
|
}
|
1352
1493
|
|
1353
1494
|
if (fileExists(responseDir + "/error/solution_description.html")) {
|
1354
|
-
e.setSolutionDescriptionHTML(
|
1495
|
+
e.setSolutionDescriptionHTML(safeReadFile(session.responseErrorDirFd,
|
1496
|
+
"solution_description.html", SPAWNINGKIT_MAX_SUBPROCESS_ERROR_MESSAGE_SIZE).first);
|
1355
1497
|
} else if (fileExists(responseDir + "/error/solution_description.txt")) {
|
1356
|
-
e.setSolutionDescriptionHTML(escapeHTML(strip(
|
1357
|
-
|
1498
|
+
e.setSolutionDescriptionHTML(escapeHTML(strip(safeReadFile(session.responseErrorDirFd,
|
1499
|
+
"solution_description.txt", SPAWNINGKIT_MAX_SUBPROCESS_ERROR_MESSAGE_SIZE).first)));
|
1358
1500
|
}
|
1359
1501
|
|
1502
|
+
loadBasicInfoFromEnvDumpDir(e);
|
1503
|
+
loadAnnotationsFromEnvDumpDir(e);
|
1504
|
+
}
|
1505
|
+
|
1506
|
+
void loadBasicInfoFromEnvDumpDir(SpawnException &e) const {
|
1507
|
+
loadBasicInfoFromEnvDumpDir(e, session);
|
1508
|
+
}
|
1509
|
+
|
1510
|
+
static void loadBasicInfoFromEnvDumpDir(SpawnException &e, HandshakeSession &session) {
|
1360
1511
|
string envvars, userInfo, ulimits;
|
1361
|
-
|
1512
|
+
|
1513
|
+
loadBasicInfoFromEnvDumpDir(session.envDumpDir, session.envDumpDirFd,
|
1514
|
+
envvars, userInfo, ulimits);
|
1362
1515
|
e.setSubprocessEnvvars(envvars);
|
1363
1516
|
e.setSubprocessUserInfo(userInfo);
|
1364
1517
|
e.setSubprocessUlimits(ulimits);
|
1365
|
-
|
1366
|
-
loadAnnotationsFromEnvDumpDir(e);
|
1367
1518
|
}
|
1368
1519
|
|
1369
1520
|
static void doClosedir(DIR *dir) {
|
@@ -1387,7 +1538,9 @@ private:
|
|
1387
1538
|
while ((ent = readdir(dir)) != NULL) {
|
1388
1539
|
if (ent->d_name[0] != '.') {
|
1389
1540
|
e.setAnnotation(ent->d_name, strip(
|
1390
|
-
|
1541
|
+
safeReadFile(session.envDumpAnnotationsDirFd,
|
1542
|
+
ent->d_name, SPAWNINGKIT_MAX_SUBPROCESS_ENVDUMP_SIZE).first
|
1543
|
+
));
|
1391
1544
|
}
|
1392
1545
|
}
|
1393
1546
|
}
|
@@ -1629,16 +1782,19 @@ public:
|
|
1629
1782
|
}
|
1630
1783
|
|
1631
1784
|
static void loadBasicInfoFromEnvDumpDir(const string &envDumpDir,
|
1632
|
-
string &envvars, string &userInfo, string &ulimits)
|
1785
|
+
int envDumpDirFd, string &envvars, string &userInfo, string &ulimits)
|
1633
1786
|
{
|
1634
1787
|
if (fileExists(envDumpDir + "/envvars")) {
|
1635
|
-
envvars =
|
1788
|
+
envvars = safeReadFile(envDumpDirFd, "envvars",
|
1789
|
+
SPAWNINGKIT_MAX_SUBPROCESS_ENVDUMP_SIZE).first;
|
1636
1790
|
}
|
1637
1791
|
if (fileExists(envDumpDir + "/user_info")) {
|
1638
|
-
userInfo =
|
1792
|
+
userInfo = safeReadFile(envDumpDirFd, "user_info",
|
1793
|
+
SPAWNINGKIT_MAX_SUBPROCESS_ENVDUMP_SIZE).first;
|
1639
1794
|
}
|
1640
1795
|
if (fileExists(envDumpDir + "/ulimits")) {
|
1641
|
-
ulimits =
|
1796
|
+
ulimits = safeReadFile(envDumpDirFd, "ulimits",
|
1797
|
+
SPAWNINGKIT_MAX_SUBPROCESS_ENVDUMP_SIZE).first;
|
1642
1798
|
}
|
1643
1799
|
}
|
1644
1800
|
};
|