passenger 5.3.1 → 5.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
};
|