passenger 5.2.3 → 5.3.0
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 +4 -4
- data/CHANGELOG +13 -0
- data/CONTRIBUTORS +5 -1
- data/build/agent.rb +22 -2
- data/build/cxx_tests.rb +41 -5
- data/build/misc.rb +4 -1
- data/build/support/cxx_dependency_map.rb +1746 -908
- data/build/support/vendor/cxx_hinted_parser/CxxHintedParser.sublime-project +8 -0
- data/build/support/vendor/cxx_hinted_parser/Gemfile +5 -0
- data/build/support/vendor/cxx_hinted_parser/Gemfile.lock +30 -0
- data/{src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core → build/support/vendor/cxx_hinted_parser}/LICENSE.md +1 -1
- data/build/support/vendor/cxx_hinted_parser/README.md +95 -0
- data/build/support/vendor/cxx_hinted_parser/Rakefile +4 -0
- data/{src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/initialize.rb → build/support/vendor/cxx_hinted_parser/lib/cxx_hinted_parser.rb} +2 -9
- data/build/support/vendor/cxx_hinted_parser/lib/cxx_hinted_parser/parser.rb +239 -0
- data/dev/ci/README.md +15 -2
- data/dev/ci/lib/set-container-envvars.sh +6 -0
- data/dev/ci/lib/setup-container.sh +4 -1
- data/dev/ci/scripts/debug-console-wrapper.sh +3 -1
- data/dev/ci/setup-host +5 -0
- data/dev/ci/tests/binaries/Jenkinsfile +105 -0
- data/dev/ci/tests/binaries/build-linux +38 -0
- data/dev/ci/tests/binaries/build-macos +40 -0
- data/dev/ci/tests/binaries/prepare-macos +38 -0
- data/dev/ci/tests/binaries/test-linux +45 -0
- data/dev/ci/tests/binaries/test-macos +38 -0
- data/dev/ci/tests/debian/Jenkinsfile +2 -2
- data/dev/ci/tests/rpm/Jenkinsfile +1 -1
- data/dev/configkit-schemas/index.json +3 -24
- data/dev/vagrant/nginx_rakefile +0 -1
- data/package.json +15 -5
- data/resources/templates/error_renderer/.editorconfig +19 -0
- data/resources/templates/error_renderer/with_details/README.md +9 -0
- data/resources/templates/error_renderer/with_details/dist/bundle.js +33 -0
- data/resources/templates/error_renderer/with_details/dist/styles.css +17 -0
- data/resources/templates/error_renderer/with_details/src/DetailsView.jsx +52 -0
- data/resources/templates/error_renderer/with_details/src/GetHelpView.jsx +61 -0
- data/resources/templates/error_renderer/with_details/src/JourneyView.css +50 -0
- data/resources/templates/error_renderer/with_details/src/JourneyView.jsx +621 -0
- data/resources/templates/error_renderer/with_details/src/PageMain.css +114 -0
- data/resources/templates/error_renderer/with_details/src/PageMain.jsx +136 -0
- data/resources/templates/error_renderer/with_details/src/ProblemDescriptionView.jsx +14 -0
- data/resources/templates/error_renderer/with_details/src/ProcessDetailsView.jsx +56 -0
- data/resources/templates/error_renderer/with_details/src/SolutionDescriptionView.css +5 -0
- data/resources/templates/error_renderer/with_details/src/SolutionDescriptionView.jsx +15 -0
- data/resources/templates/error_renderer/with_details/src/SummaryView.jsx +35 -0
- data/resources/templates/error_renderer/with_details/src/SystemComponentView.css +34 -0
- data/resources/templates/error_renderer/with_details/src/SystemComponentView.jsx +168 -0
- data/resources/templates/error_renderer/with_details/src/SystemComponentsView.css +13 -0
- data/resources/templates/error_renderer/with_details/src/SystemComponentsView.jsx +116 -0
- data/resources/templates/error_renderer/with_details/src/Tab.jsx +12 -0
- data/resources/templates/error_renderer/with_details/src/Tabs.jsx +104 -0
- data/resources/templates/error_renderer/with_details/src/bootstrap/bootstrap.css +3446 -0
- data/resources/templates/error_renderer/with_details/src/bootstrap/bootstrap.js +293 -0
- data/resources/templates/error_renderer/with_details/src/bootstrap/config.json +401 -0
- data/resources/templates/error_renderer/with_details/src/index.html.template +22 -0
- data/resources/templates/error_renderer/with_details/src/index.jsx +23 -0
- data/resources/templates/error_renderer/with_details/webpack.config.js +47 -0
- data/resources/templates/error_renderer/without_details/dist/bundle.js +1 -0
- data/resources/templates/error_renderer/without_details/dist/styles.css +1 -0
- data/resources/templates/{undisclosed_error.html.template → error_renderer/without_details/src/index.html.template} +7 -11
- data/resources/templates/error_renderer/without_details/src/index.js +1 -0
- data/resources/templates/{error_layout.css → error_renderer/without_details/src/main.css} +5 -2
- data/resources/templates/error_renderer/without_details/webpack.config.js +42 -0
- data/src/agent/AgentMain.cpp +3 -3
- data/src/agent/Core/ApplicationPool/BasicProcessInfo.h +13 -0
- data/src/agent/Core/ApplicationPool/Common.h +3 -4
- data/src/agent/Core/ApplicationPool/Context.h +27 -17
- data/src/agent/Core/ApplicationPool/Group.h +3 -1
- data/src/agent/Core/ApplicationPool/Group/InitializationAndShutdown.cpp +2 -12
- data/src/agent/Core/ApplicationPool/Group/InternalUtils.cpp +55 -10
- data/src/agent/Core/ApplicationPool/Group/LifetimeAndBasics.cpp +1 -1
- data/src/agent/Core/ApplicationPool/Group/OutOfBandWork.cpp +1 -1
- data/src/agent/Core/ApplicationPool/Group/SpawningAndRestarting.cpp +13 -6
- data/src/agent/Core/ApplicationPool/Implementation.cpp +16 -100
- data/src/agent/Core/ApplicationPool/Options.h +8 -65
- data/src/agent/Core/ApplicationPool/Pool.h +4 -21
- data/src/agent/Core/ApplicationPool/Pool/AnalyticsCollection.cpp +1 -60
- data/src/agent/Core/ApplicationPool/Pool/GeneralUtils.cpp +10 -13
- data/src/agent/Core/ApplicationPool/Pool/InitializationAndShutdown.cpp +3 -8
- data/src/agent/Core/ApplicationPool/Pool/Miscellaneous.cpp +2 -34
- data/src/agent/Core/ApplicationPool/Pool/StateInspection.cpp +1 -1
- data/src/agent/Core/ApplicationPool/Process.cpp +17 -12
- data/src/agent/Core/ApplicationPool/Process.h +146 -93
- data/src/agent/Core/ApplicationPool/Session.h +2 -2
- data/src/agent/Core/ApplicationPool/Socket.h +28 -27
- data/src/agent/Core/Config.h +1 -3
- data/src/agent/Core/ConfigChange.cpp +2 -4
- data/src/agent/Core/Controller.h +2 -8
- data/src/agent/Core/Controller/BufferBody.cpp +0 -2
- data/src/agent/Core/Controller/CheckoutSession.cpp +12 -24
- data/src/agent/Core/Controller/Config.h +1 -9
- data/src/agent/Core/Controller/ForwardResponse.cpp +0 -34
- data/src/agent/Core/Controller/Hooks.cpp +0 -7
- data/src/agent/Core/Controller/InitRequest.cpp +0 -43
- data/src/agent/Core/Controller/InitializationAndShutdown.cpp +0 -4
- data/src/agent/Core/Controller/Request.h +1 -35
- data/src/agent/Core/Controller/SendRequest.cpp +0 -32
- data/src/agent/Core/CoreMain.cpp +19 -32
- data/src/agent/Core/SpawningKit/Config.h +329 -55
- data/src/agent/Core/SpawningKit/Config/AutoGeneratedCode.h +369 -0
- data/src/agent/Core/SpawningKit/Config/AutoGeneratedCode.h.cxxcodebuilder +307 -0
- data/src/agent/Core/SpawningKit/Context.h +211 -0
- data/src/agent/Core/SpawningKit/DirectSpawner.h +112 -122
- data/src/agent/Core/SpawningKit/DummySpawner.h +59 -20
- data/src/agent/Core/SpawningKit/ErrorRenderer.h +117 -0
- data/src/agent/Core/SpawningKit/Exceptions.h +1157 -0
- data/src/agent/Core/SpawningKit/Factory.h +24 -17
- data/src/agent/Core/SpawningKit/{BackgroundIOCapturer.h → Handshake/BackgroundIOCapturer.h} +48 -18
- data/src/agent/Core/SpawningKit/Handshake/Perform.h +1650 -0
- data/src/agent/Core/SpawningKit/Handshake/Prepare.h +582 -0
- data/src/agent/Core/SpawningKit/Handshake/Session.h +91 -0
- data/src/agent/Core/SpawningKit/Handshake/WorkDir.h +100 -0
- data/src/agent/Core/SpawningKit/Journey.h +561 -0
- data/src/agent/Core/SpawningKit/PipeWatcher.h +41 -18
- data/src/agent/Core/SpawningKit/README.md +534 -0
- data/src/agent/Core/SpawningKit/Result.h +182 -7
- data/src/agent/Core/SpawningKit/Result/AutoGeneratedCode.h +69 -0
- data/src/agent/Core/SpawningKit/Result/AutoGeneratedCode.h.cxxcodebuilder +110 -0
- data/src/agent/Core/SpawningKit/SmartSpawner.h +1027 -562
- data/src/agent/Core/SpawningKit/Spawner.h +70 -1134
- data/src/agent/Core/SpawningKit/UserSwitchingRules.h +3 -33
- data/src/agent/README.md +2 -3
- data/src/agent/Shared/ApiServerUtils.h +2 -3
- data/src/agent/SpawnEnvSetupper/SpawnEnvSetupperMain.cpp +932 -0
- data/src/agent/Watchdog/Config.h +1 -3
- data/src/agent/Watchdog/WatchdogMain.cpp +2 -1
- data/src/apache2_module/ConfigGeneral/AutoGeneratedDefinitions.cpp +5 -0
- data/src/apache2_module/ConfigGeneral/AutoGeneratedManifestDefaultsInitialization.cpp +5 -0
- data/src/apache2_module/ConfigGeneral/ManifestGeneration.h +22 -13
- data/src/apache2_module/DirConfig/AutoGeneratedCreateFunction.cpp +5 -0
- data/src/apache2_module/DirConfig/AutoGeneratedHeaderSerialization.cpp +3 -0
- data/src/apache2_module/DirConfig/AutoGeneratedManifestGeneration.cpp +13 -0
- data/src/apache2_module/DirConfig/AutoGeneratedMergeFunction.cpp +7 -0
- data/src/apache2_module/DirConfig/AutoGeneratedStruct.h +13 -0
- data/src/cxx_supportlib/Constants.h +3 -1
- data/src/cxx_supportlib/Exceptions.h +0 -121
- data/src/cxx_supportlib/LoggingKit/Implementation.cpp +7 -6
- data/src/cxx_supportlib/LoggingKit/Logging.h +3 -1
- data/src/cxx_supportlib/Utils.cpp +42 -0
- data/src/cxx_supportlib/Utils.h +7 -0
- data/src/cxx_supportlib/Utils/IOUtils.cpp +58 -0
- data/src/cxx_supportlib/Utils/IOUtils.h +13 -0
- data/src/cxx_supportlib/Utils/JsonUtils.h +130 -23
- data/src/cxx_supportlib/Utils/ScopeGuard.h +9 -4
- data/src/cxx_supportlib/Utils/StrIntUtils.cpp +7 -0
- data/src/cxx_supportlib/Utils/StrIntUtils.h +1 -0
- data/src/cxx_supportlib/Utils/SystemTime.h +1 -1
- data/src/cxx_supportlib/Utils/Timer.h +1 -1
- data/src/cxx_supportlib/WebSocketCommandReverseServer.h +6 -4
- data/src/cxx_supportlib/vendor-copy/adhoc_lve.h +1 -0
- data/src/helper-scripts/node-loader.js +54 -59
- data/src/helper-scripts/rack-loader.rb +63 -60
- data/src/helper-scripts/rack-preloader.rb +125 -72
- data/src/helper-scripts/wsgi-loader.py +100 -43
- data/src/nginx_module/ConfigGeneral/AutoGeneratedDefinitions.c +120 -112
- data/src/nginx_module/ConfigGeneral/AutoGeneratedManifestDefaultsInitialization.c +15 -8
- data/src/nginx_module/ConfigGeneral/AutoGeneratedSetterFuncs.c +142 -142
- data/src/nginx_module/ConfigGeneral/ManifestGeneration.c +26 -15
- data/src/nginx_module/ConfigGeneral/ManifestGeneration.h +3 -0
- data/src/nginx_module/LocationConfig/AutoGeneratedCreateFunction.c +76 -70
- data/src/nginx_module/LocationConfig/AutoGeneratedHeaderSerialization.c +114 -99
- data/src/nginx_module/LocationConfig/AutoGeneratedManifestGeneration.c +170 -156
- data/src/nginx_module/LocationConfig/AutoGeneratedMergeFunction.c +38 -35
- data/src/nginx_module/LocationConfig/AutoGeneratedStruct.h +5 -1
- data/src/ruby_supportlib/phusion_passenger.rb +5 -5
- data/src/ruby_supportlib/phusion_passenger/admin_tools/instance.rb +14 -1
- data/src/ruby_supportlib/phusion_passenger/apache2/config_options.rb +8 -0
- data/src/ruby_supportlib/phusion_passenger/common_library.rb +0 -3
- data/src/ruby_supportlib/phusion_passenger/config/nginx_engine_compiler.rb +0 -1
- data/src/ruby_supportlib/phusion_passenger/constants.rb +2 -0
- data/src/ruby_supportlib/phusion_passenger/loader_shared_helpers.rb +646 -238
- data/src/ruby_supportlib/phusion_passenger/nginx/config_options.rb +117 -95
- data/src/ruby_supportlib/phusion_passenger/packaging.rb +0 -1
- data/src/ruby_supportlib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +5 -1
- data/src/ruby_supportlib/phusion_passenger/preloader_shared_helpers.rb +92 -69
- data/src/ruby_supportlib/phusion_passenger/public_api.rb +0 -17
- data/src/ruby_supportlib/phusion_passenger/rack/thread_handler_extension.rb +0 -3
- data/src/ruby_supportlib/phusion_passenger/request_handler.rb +4 -5
- data/src/ruby_supportlib/phusion_passenger/request_handler/thread_handler.rb +0 -22
- metadata +64 -67
- data/resources/templates/error_layout.html.template +0 -86
- data/resources/templates/general_error.html.template +0 -1
- data/resources/templates/general_error_with_html.html.template +0 -1
- data/src/agent/Core/ApplicationPool/ErrorRenderer.h +0 -131
- data/src/agent/Core/SpawningKit/Options.h +0 -41
- data/src/agent/Core/UnionStation/Connection.h +0 -173
- data/src/agent/Core/UnionStation/Context.h +0 -536
- data/src/agent/Core/UnionStation/StopwatchLog.h +0 -147
- data/src/agent/Core/UnionStation/Transaction.h +0 -249
- data/src/agent/SpawnPreparer/SpawnPreparerMain.cpp +0 -208
- data/src/cxx_supportlib/UnionStationFilterSupport.cpp +0 -67
- data/src/cxx_supportlib/UnionStationFilterSupport.h +0 -1622
- data/src/nodejs_supportlib/phusion_passenger/log_express.js +0 -106
- data/src/nodejs_supportlib/phusion_passenger/log_mongodb.js +0 -202
- data/src/nodejs_supportlib/phusion_passenger/ustreporter.js +0 -227
- data/src/nodejs_supportlib/phusion_passenger/ustrouter_connector.js +0 -448
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/CONFIG.md +0 -37
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/Gemfile +0 -17
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/Gemfile.lock +0 -59
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/README-API.md +0 -5
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/README.md +0 -117
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/Rakefile +0 -115
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core.rb +0 -423
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/api.rb +0 -238
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/connection.rb +0 -67
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/context.rb +0 -281
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/lock.rb +0 -62
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/log.rb +0 -66
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/message_channel.rb +0 -157
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter.rb +0 -150
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/basics.rb +0 -199
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/controllers.rb +0 -187
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/misc.rb +0 -303
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/view_rendering.rb +0 -91
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/simple_json.rb +0 -396
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/spec_helper.rb +0 -279
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/time_point.rb +0 -39
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/transaction.rb +0 -173
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/utils.rb +0 -177
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/version.rb +0 -32
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/version_data.rb +0 -44
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml.example +0 -16
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml.travis +0 -20
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml.travis-with-sudo +0 -18
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/union_station_hooks_core.gemspec +0 -23
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/Gemfile +0 -14
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/Gemfile.lock +0 -45
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/LICENSE.md +0 -19
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/README.md +0 -104
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/Rakefile +0 -160
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails.rb +0 -200
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/action_controller_extension.rb +0 -45
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/action_view_subscriber.rb +0 -55
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/active_record_subscriber.rb +0 -41
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/active_support_benchmarkable_extension.rb +0 -47
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/active_support_cache_subscriber.rb +0 -79
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/exception_logger.rb +0 -57
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/version.rb +0 -32
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/version_data.rb +0 -44
- data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/union_station_hooks_rails.gemspec +0 -34
@@ -26,81 +26,17 @@
|
|
26
26
|
#ifndef _PASSENGER_SPAWNING_KIT_SPAWNER_H_
|
27
27
|
#define _PASSENGER_SPAWNING_KIT_SPAWNER_H_
|
28
28
|
|
29
|
-
|
30
|
-
* This file implements application spawning support. Several classes
|
31
|
-
* are provided which all implement the Spawner interface. The spawn()
|
32
|
-
* method spawns an application process based on the given options
|
33
|
-
* and returns a Process object which contains information about the
|
34
|
-
* spawned process.
|
35
|
-
*
|
36
|
-
* The DirectSpawner class spawns application processes directly.
|
37
|
-
*
|
38
|
-
* The SmartSpawner class spawns application processes through a
|
39
|
-
* preloader process. The preloader process loads the application
|
40
|
-
* code into its address space and then listens on a socket for spawn
|
41
|
-
* commands. Upon receiving a spawn command, it will fork() itself.
|
42
|
-
* This makes spawning multiple application processes much faster.
|
43
|
-
* Note that a single SmartSpawner instance is only usable for a
|
44
|
-
* single application.
|
45
|
-
*
|
46
|
-
* DummySpawner doesn't do anything. It returns dummy Process objects.
|
47
|
-
*
|
48
|
-
* DirectSpawner, SmartSpawner and DummySpawner all implement the Spawner interface.
|
49
|
-
*
|
50
|
-
* SpawnerFactory is a convenience class which takes an Options objects
|
51
|
-
* and figures out, based on options.spawnMethod, whether to create
|
52
|
-
* a DirectSpawner or a SmartSpawner. In case of the smart spawning
|
53
|
-
* method, SpawnerFactory also automatically figures out which preloader
|
54
|
-
* to use based on options.appType.
|
55
|
-
*/
|
56
|
-
|
57
|
-
#include <string>
|
58
|
-
#include <map>
|
59
|
-
#include <vector>
|
60
|
-
#include <utility>
|
61
|
-
#include <algorithm>
|
62
|
-
#include <boost/make_shared.hpp>
|
63
|
-
#include <boost/shared_array.hpp>
|
64
|
-
#include <boost/bind.hpp>
|
65
|
-
#include <boost/foreach.hpp>
|
66
|
-
#include <boost/move/move.hpp>
|
29
|
+
#include <boost/shared_ptr.hpp>
|
67
30
|
#include <oxt/system_calls.hpp>
|
68
|
-
|
69
|
-
#include <sys/types.h>
|
70
|
-
#include <sys/socket.h>
|
71
|
-
#include <sys/un.h>
|
72
|
-
#include <cstdio>
|
73
|
-
#include <cstdlib>
|
74
|
-
#include <cstring>
|
75
|
-
#include <cerrno>
|
76
|
-
#include <cassert>
|
77
|
-
#include <unistd.h>
|
78
|
-
#include <pwd.h>
|
79
|
-
#include <grp.h>
|
80
|
-
#include <dirent.h>
|
81
|
-
#include <adhoc_lve.h>
|
31
|
+
|
82
32
|
#include <modp_b64.h>
|
83
|
-
|
84
|
-
#include <
|
85
|
-
#include <
|
86
|
-
#include <
|
87
|
-
#include <Utils/BufferedIO.h>
|
88
|
-
#include <Utils/ScopeGuard.h>
|
89
|
-
#include <Utils/Timer.h>
|
90
|
-
#include <Utils/IOUtils.h>
|
91
|
-
#include <Utils/StrIntUtils.h>
|
92
|
-
#include <Utils/ProcessMetricsCollector.h>
|
93
|
-
#include <Core/SpawningKit/Config.h>
|
94
|
-
#include <Core/SpawningKit/Options.h>
|
33
|
+
|
34
|
+
#include <LoggingKit/Logging.h>
|
35
|
+
#include <Utils/SystemTime.h>
|
36
|
+
#include <Core/SpawningKit/Context.h>
|
95
37
|
#include <Core/SpawningKit/Result.h>
|
96
|
-
#include <Core/SpawningKit/BackgroundIOCapturer.h>
|
97
38
|
#include <Core/SpawningKit/UserSwitchingRules.h>
|
98
39
|
|
99
|
-
namespace tut {
|
100
|
-
struct ApplicationPool2_DirectSpawnerTest;
|
101
|
-
struct ApplicationPool2_SmartSpawnerTest;
|
102
|
-
}
|
103
|
-
|
104
40
|
namespace Passenger {
|
105
41
|
namespace SpawningKit {
|
106
42
|
|
@@ -110,713 +46,40 @@ using namespace oxt;
|
|
110
46
|
|
111
47
|
|
112
48
|
class Spawner {
|
113
|
-
protected:
|
114
|
-
friend struct tut::ApplicationPool2_DirectSpawnerTest;
|
115
|
-
friend struct tut::ApplicationPool2_SmartSpawnerTest;
|
116
|
-
|
117
|
-
/**
|
118
|
-
* A temporary directory for spawned child processes to write
|
119
|
-
* debugging information to. It is removed after spawning has
|
120
|
-
* determined to be successful or failed.
|
121
|
-
*/
|
122
|
-
class DebugDir {
|
123
|
-
private:
|
124
|
-
string path;
|
125
|
-
|
126
|
-
static void doClosedir(DIR *dir) {
|
127
|
-
closedir(dir);
|
128
|
-
}
|
129
|
-
|
130
|
-
public:
|
131
|
-
DebugDir(uid_t uid, gid_t gid) {
|
132
|
-
char buf[PATH_MAX];
|
133
|
-
char *pos = buf;
|
134
|
-
const char *end = buf + PATH_MAX;
|
135
|
-
|
136
|
-
pos = appendData(pos, end, getSystemTempDir());
|
137
|
-
pos = appendData(pos, end, "/passenger.spawn-debug.XXXXXXXXXX");
|
138
|
-
*pos = '\0';
|
139
|
-
|
140
|
-
const char *result = mkdtemp(buf);
|
141
|
-
if (result == NULL) {
|
142
|
-
int e = errno;
|
143
|
-
throw SystemException("Cannot create a temporary directory "
|
144
|
-
"in the format of '" + StaticString(buf) + "'", e);
|
145
|
-
} else {
|
146
|
-
path = result;
|
147
|
-
boost::this_thread::disable_interruption di;
|
148
|
-
boost::this_thread::disable_syscall_interruption dsi;
|
149
|
-
syscalls::chown(result, uid, gid);
|
150
|
-
}
|
151
|
-
}
|
152
|
-
|
153
|
-
~DebugDir() {
|
154
|
-
removeDirTree(path);
|
155
|
-
}
|
156
|
-
|
157
|
-
const string &getPath() const {
|
158
|
-
return path;
|
159
|
-
}
|
160
|
-
|
161
|
-
map<string, string> readAll() {
|
162
|
-
map<string, string> result;
|
163
|
-
DIR *dir = opendir(path.c_str());
|
164
|
-
ScopeGuard guard(boost::bind(doClosedir, dir));
|
165
|
-
struct dirent *ent;
|
166
|
-
|
167
|
-
while ((ent = readdir(dir)) != NULL) {
|
168
|
-
if (ent->d_name[0] != '.') {
|
169
|
-
try {
|
170
|
-
result.insert(make_pair<string, string>(
|
171
|
-
ent->d_name,
|
172
|
-
Passenger::readAll(path + "/" + ent->d_name)));
|
173
|
-
} catch (const SystemException &) {
|
174
|
-
// Do nothing.
|
175
|
-
}
|
176
|
-
}
|
177
|
-
}
|
178
|
-
return result;
|
179
|
-
}
|
180
|
-
};
|
181
|
-
|
182
|
-
typedef boost::shared_ptr<DebugDir> DebugDirPtr;
|
183
|
-
|
184
|
-
/**
|
185
|
-
* Contains information that will be used after fork()ing but before exec()ing,
|
186
|
-
* such as the intended app root, the UID it should switch to, the
|
187
|
-
* groups it should assume, etc. This structure is allocated before forking
|
188
|
-
* because after forking and before exec() it may not be safe to allocate memory.
|
189
|
-
*/
|
190
|
-
struct SpawnPreparationInfo {
|
191
|
-
// General
|
192
|
-
|
193
|
-
/** Absolute application root path. */
|
194
|
-
string appRoot;
|
195
|
-
/** Absolute pre-exec chroot path. If no chroot is configured, then this is "/". */
|
196
|
-
string chrootDir;
|
197
|
-
/** Absolute application root path inside the chroot. If no chroot is
|
198
|
-
* configured then this is is equal to appRoot. */
|
199
|
-
string appRootInsideChroot;
|
200
|
-
/** A list of all parent directories of the appRoot, as well as appRoot itself.
|
201
|
-
* The pre-exec chroot directory is included, and this list goes no futher than that.
|
202
|
-
* For example if appRoot is /var/jail/foo/bar/baz and the chroot is /var/jail,
|
203
|
-
* then this list contains:
|
204
|
-
* /var/jail/foo
|
205
|
-
* /var/jail/foo/bar
|
206
|
-
* /var/jail/foo/bar/baz
|
207
|
-
*/
|
208
|
-
vector<string> appRootPaths;
|
209
|
-
/** Same as appRootPaths, but without the chroot component. For example if
|
210
|
-
* appRoot is /var/jail/foo/bar/baz and the chroot is /var/jail, then this list
|
211
|
-
* contains:
|
212
|
-
* /foo
|
213
|
-
* /foo/bar
|
214
|
-
* /foo/bar/baz
|
215
|
-
*/
|
216
|
-
vector<string> appRootPathsInsideChroot;
|
217
|
-
|
218
|
-
UserSwitchingInfo userSwitching;
|
219
|
-
|
220
|
-
// Other information
|
221
|
-
string codeRevision;
|
222
|
-
};
|
223
|
-
|
224
|
-
/**
|
225
|
-
* Structure containing arguments and working state for negotiating
|
226
|
-
* the spawning protocol.
|
227
|
-
*/
|
228
|
-
struct NegotiationDetails {
|
229
|
-
/****** Arguments ******/
|
230
|
-
|
231
|
-
/** The preparation info of the process we're negotiating with. It's used
|
232
|
-
* by security validators to check whether the information sent back by the
|
233
|
-
* process make any sense. */
|
234
|
-
SpawnPreparationInfo *preparation;
|
235
|
-
/** This object captures the process's stderr while negotiation is in progress.
|
236
|
-
* (Recall that negotiation is performed over the process's stdout while stderr
|
237
|
-
* is used purely for outputting messages.)
|
238
|
-
* If the negotiation protocol fails, then any output captured by this object
|
239
|
-
* will be stored into the resulting SpawnException's error page. */
|
240
|
-
BackgroundIOCapturerPtr stderrCapturer;
|
241
|
-
/** The PID of the process we're negotiating with. */
|
242
|
-
pid_t pid;
|
243
|
-
FileDescriptor adminSocket;
|
244
|
-
FileDescriptor errorPipe;
|
245
|
-
const Options *options;
|
246
|
-
DebugDirPtr debugDir;
|
247
|
-
|
248
|
-
/****** Working state ******/
|
249
|
-
BufferedIO io;
|
250
|
-
string gupid;
|
251
|
-
unsigned long long spawnStartTime;
|
252
|
-
unsigned long long timeout;
|
253
|
-
|
254
|
-
NegotiationDetails() {
|
255
|
-
preparation = NULL;
|
256
|
-
pid = 0;
|
257
|
-
options = NULL;
|
258
|
-
spawnStartTime = 0;
|
259
|
-
timeout = 0;
|
260
|
-
}
|
261
|
-
};
|
262
|
-
|
263
49
|
private:
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
output.append(value.data(), value.size());
|
273
|
-
output.append(1, '\0');
|
274
|
-
}
|
275
|
-
|
276
|
-
void sendSpawnRequest(NegotiationDetails &details) {
|
277
|
-
TRACE_POINT();
|
278
|
-
try {
|
279
|
-
const size_t UNIX_PATH_MAX = sizeof(((struct sockaddr_un *) 0)->sun_path);
|
280
|
-
string data = "You have control 1.0\n"
|
281
|
-
"passenger_root: " + config->resourceLocator->getInstallSpec() + "\n"
|
282
|
-
"passenger_version: " PASSENGER_VERSION "\n"
|
283
|
-
"ruby_libdir: " + config->resourceLocator->getRubyLibDir() + "\n"
|
284
|
-
"gupid: " + details.gupid + "\n"
|
285
|
-
"UNIX_PATH_MAX: " + toString(UNIX_PATH_MAX) + "\n";
|
286
|
-
if (!details.options->apiKey.empty()) {
|
287
|
-
data.append("connect_password: " + details.options->apiKey + "\n");
|
288
|
-
}
|
289
|
-
if (!config->instanceDir.empty()) {
|
290
|
-
data.append("instance_dir: " + config->instanceDir + "\n");
|
291
|
-
data.append("socket_dir: " + config->instanceDir + "/apps.s\n");
|
292
|
-
}
|
293
|
-
|
294
|
-
vector<string> args;
|
295
|
-
vector<string>::const_iterator it, end;
|
296
|
-
details.options->toVector(args, *config->resourceLocator, Options::SPAWN_OPTIONS);
|
297
|
-
for (it = args.begin(); it != args.end(); it++) {
|
298
|
-
const string &key = *it;
|
299
|
-
it++;
|
300
|
-
const string &value = *it;
|
301
|
-
data.append(key + ": " + value + "\n");
|
302
|
-
}
|
303
|
-
|
304
|
-
vector<StaticString> lines;
|
305
|
-
split(data, '\n', lines);
|
306
|
-
foreach (const StaticString line, lines) {
|
307
|
-
P_DEBUG("[App " << details.pid << " stdin >>] " << line);
|
308
|
-
}
|
309
|
-
writeExact(details.adminSocket, data, &details.timeout);
|
310
|
-
writeExact(details.adminSocket, "\n", &details.timeout);
|
311
|
-
} catch (const SystemException &e) {
|
312
|
-
if (e.code() == EPIPE) {
|
313
|
-
/* Ignore this. Process might have written an
|
314
|
-
* error response before reading the arguments,
|
315
|
-
* in which case we'll want to show that instead.
|
316
|
-
*/
|
317
|
-
} else {
|
318
|
-
throw;
|
319
|
-
}
|
320
|
-
}
|
321
|
-
}
|
322
|
-
|
323
|
-
Result handleSpawnResponse(NegotiationDetails &details) {
|
324
|
-
TRACE_POINT();
|
325
|
-
Json::Value sockets;
|
326
|
-
Result result;
|
327
|
-
|
328
|
-
while (true) {
|
329
|
-
string line;
|
330
|
-
|
331
|
-
try {
|
332
|
-
line = readMessageLine(details);
|
333
|
-
} catch (const SystemException &e) {
|
334
|
-
throwAppSpawnException("An error occurred while starting the "
|
335
|
-
"web application. There was an I/O error while reading its "
|
336
|
-
"startup response: " + e.sys(),
|
337
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
338
|
-
details);
|
339
|
-
} catch (const TimeoutException &) {
|
340
|
-
throwAppSpawnException("An error occurred while starting the "
|
341
|
-
"web application: it did not write a startup response in time. "
|
342
|
-
"If your app needs more time to start you can increase the "
|
343
|
-
"Passenger start timeout config option.",
|
344
|
-
SpawnException::APP_STARTUP_TIMEOUT,
|
345
|
-
details);
|
346
|
-
}
|
347
|
-
|
348
|
-
if (line.empty()) {
|
349
|
-
throwAppSpawnException("An error occurred while starting the "
|
350
|
-
"web application. It unexpectedly closed the connection while "
|
351
|
-
"sending its startup response.",
|
352
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
353
|
-
details);
|
354
|
-
} else if (line[line.size() - 1] != '\n') {
|
355
|
-
throwAppSpawnException("An error occurred while starting the "
|
356
|
-
"web application. It sent a line without a newline character "
|
357
|
-
"in its startup response.",
|
358
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
359
|
-
details);
|
360
|
-
} else if (line == "\n") {
|
50
|
+
StringKeyTable<StaticString> decodeEnvironmentVariables(const StaticString &envvarsData) {
|
51
|
+
StringKeyTable<StaticString> result;
|
52
|
+
string::size_type keyStart = 0;
|
53
|
+
|
54
|
+
while (keyStart < envvarsData.size()) {
|
55
|
+
string::size_type keyEnd = envvarsData.find('\0', keyStart);
|
56
|
+
string::size_type valueStart = keyEnd + 1;
|
57
|
+
if (valueStart >= envvarsData.size()) {
|
361
58
|
break;
|
362
59
|
}
|
363
60
|
|
364
|
-
string::size_type
|
365
|
-
if (
|
366
|
-
|
367
|
-
"web application. It sent a startup response line without "
|
368
|
-
"separator.",
|
369
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
370
|
-
details);
|
371
|
-
}
|
372
|
-
|
373
|
-
string key = line.substr(0, pos);
|
374
|
-
string value = line.substr(pos + 2, line.size() - pos - 3);
|
375
|
-
if (key == "socket") {
|
376
|
-
// socket: <name>;<address>;<protocol>;<concurrency>
|
377
|
-
// TODO: in case of TCP sockets, check whether it points to localhost
|
378
|
-
// TODO: in case of unix sockets, check whether filename is absolute
|
379
|
-
// and whether owner is correct
|
380
|
-
vector<string> args;
|
381
|
-
split(value, ';', args);
|
382
|
-
if (args.size() == 4) {
|
383
|
-
string error = validateSocketAddress(details, args[1]);
|
384
|
-
if (!error.empty()) {
|
385
|
-
throwAppSpawnException(
|
386
|
-
"An error occurred while starting the web application. " + error,
|
387
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
388
|
-
details);
|
389
|
-
}
|
390
|
-
|
391
|
-
Json::Value socket;
|
392
|
-
socket["name"] = args[0];
|
393
|
-
socket["address"] = fixupSocketAddress(*details.options, args[1]);
|
394
|
-
socket["protocol"] = args[2];
|
395
|
-
socket["concurrency"] = atoi(args[3]);
|
396
|
-
sockets.append(socket);
|
397
|
-
} else {
|
398
|
-
throwAppSpawnException("An error occurred while starting the "
|
399
|
-
"web application. It reported a wrongly formatted 'socket'"
|
400
|
-
"response value: '" + value + "'",
|
401
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
402
|
-
details);
|
403
|
-
}
|
404
|
-
} else if (key == "pid") {
|
405
|
-
// pid: <PID>
|
406
|
-
pid_t pid = atoi(value);
|
407
|
-
ProcessMetricsCollector collector;
|
408
|
-
vector<pid_t> pids;
|
409
|
-
|
410
|
-
pids.push_back(pid);
|
411
|
-
ProcessMetricMap metrics = collector.collect(pids);
|
412
|
-
if (metrics[pid].uid != details.preparation->userSwitching.uid) {
|
413
|
-
throwAppSpawnException("An error occurred while starting the "
|
414
|
-
"web application. The PID that the loader has returned does "
|
415
|
-
"not have the same UID as the loader itself.",
|
416
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
417
|
-
details);
|
418
|
-
}
|
419
|
-
details.pid = pid;
|
420
|
-
} else {
|
421
|
-
throwAppSpawnException("An error occurred while starting the "
|
422
|
-
"web application. It sent an unknown startup response line "
|
423
|
-
"called '" + key + "'.",
|
424
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
425
|
-
details);
|
61
|
+
string::size_type valueEnd = envvarsData.find('\0', valueStart);
|
62
|
+
if (valueEnd >= envvarsData.size()) {
|
63
|
+
break;
|
426
64
|
}
|
427
|
-
}
|
428
65
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
details);
|
66
|
+
StaticString key = envvarsData.substr(keyStart, keyEnd - keyStart);
|
67
|
+
StaticString value = envvarsData.substr(valueStart, valueEnd - valueStart);
|
68
|
+
result.insert(key, value, true);
|
69
|
+
keyStart = valueEnd + 1;
|
434
70
|
}
|
435
71
|
|
436
|
-
result
|
437
|
-
result["pid"] = (int) details.pid;
|
438
|
-
result["gupid"] = details.gupid;
|
439
|
-
result["sockets"] = sockets;
|
440
|
-
result["code_revision"] = details.preparation->codeRevision;
|
441
|
-
result["spawner_creation_time"] = (Json::UInt64) creationTime;
|
442
|
-
result["spawn_start_time"] = (Json::UInt64) details.spawnStartTime;
|
443
|
-
result.adminSocket = details.adminSocket;
|
444
|
-
result.errorPipe = details.errorPipe;
|
72
|
+
result.compact();
|
445
73
|
return result;
|
446
74
|
}
|
447
75
|
|
448
|
-
bool hasSessionSockets(const Json::Value &sockets) const {
|
449
|
-
Json::Value::const_iterator it, end = sockets.end();
|
450
|
-
|
451
|
-
for (it = sockets.begin(); it != end; it++) {
|
452
|
-
const Json::Value &socket = *it;
|
453
|
-
if (socket["protocol"] == "session" || socket["protocol"] == "http_session") {
|
454
|
-
return true;
|
455
|
-
}
|
456
|
-
}
|
457
|
-
return false;
|
458
|
-
}
|
459
|
-
|
460
76
|
protected:
|
461
|
-
|
462
|
-
|
463
|
-
static void nonInterruptableKillAndWaitpid(pid_t pid) {
|
464
|
-
boost::this_thread::disable_syscall_interruption dsi;
|
465
|
-
syscalls::kill(pid, SIGKILL);
|
466
|
-
syscalls::waitpid(pid, NULL, 0);
|
467
|
-
}
|
468
|
-
|
469
|
-
/**
|
470
|
-
* Behaves like <tt>waitpid(pid, status, WNOHANG)</tt>, but waits at most
|
471
|
-
* <em>timeout</em> miliseconds for the process to exit.
|
472
|
-
*/
|
473
|
-
static int timedWaitpid(pid_t pid, int *status, unsigned long long timeout) {
|
474
|
-
Timer<SystemTime::GRAN_10MSEC> timer;
|
475
|
-
int ret;
|
476
|
-
|
477
|
-
do {
|
478
|
-
ret = syscalls::waitpid(pid, status, WNOHANG);
|
479
|
-
if (ret > 0 || ret == -1) {
|
480
|
-
return ret;
|
481
|
-
} else {
|
482
|
-
syscalls::usleep(10000);
|
483
|
-
}
|
484
|
-
} while (timer.elapsed() < timeout);
|
485
|
-
return 0; // timed out
|
486
|
-
}
|
487
|
-
|
488
|
-
static string fixupSocketAddress(const Options &options, const string &address) {
|
489
|
-
TRACE_POINT();
|
490
|
-
if (!options.preexecChroot.empty() && !options.postexecChroot.empty()) {
|
491
|
-
ServerAddressType type = getSocketAddressType(address);
|
492
|
-
if (type == SAT_UNIX) {
|
493
|
-
string filename = parseUnixSocketAddress(address);
|
494
|
-
string fixedAddress = "unix:";
|
495
|
-
if (!options.preexecChroot.empty()) {
|
496
|
-
fixedAddress.append(options.preexecChroot.data(),
|
497
|
-
options.preexecChroot.size());
|
498
|
-
}
|
499
|
-
if (!options.postexecChroot.empty()) {
|
500
|
-
fixedAddress.append(options.postexecChroot.data(),
|
501
|
-
options.postexecChroot.size());
|
502
|
-
}
|
503
|
-
fixedAddress.append(filename);
|
504
|
-
return fixedAddress;
|
505
|
-
} else {
|
506
|
-
return address;
|
507
|
-
}
|
508
|
-
} else {
|
509
|
-
return address;
|
510
|
-
}
|
511
|
-
}
|
77
|
+
Context *context;
|
512
78
|
|
513
|
-
|
514
|
-
|
515
|
-
return false;
|
516
|
-
} else {
|
517
|
-
vector<string> components;
|
518
|
-
string component;
|
519
|
-
|
520
|
-
split(path, '/', components);
|
521
|
-
components.erase(components.begin());
|
522
|
-
foreach (component, components) {
|
523
|
-
if (component.empty() || component == "." || component == "..") {
|
524
|
-
return false;
|
525
|
-
}
|
526
|
-
}
|
527
|
-
return true;
|
528
|
-
}
|
529
|
-
}
|
530
|
-
|
531
|
-
/**
|
532
|
-
* Given a 'socket:' information string obtained from the spawned process,
|
533
|
-
* validates whether it is correct.
|
534
|
-
*/
|
535
|
-
string validateSocketAddress(NegotiationDetails &details, const string &_address) const {
|
536
|
-
string address = _address;
|
537
|
-
stringstream error;
|
538
|
-
|
539
|
-
switch (getSocketAddressType(address)) {
|
540
|
-
case SAT_UNIX: {
|
541
|
-
address = fixupSocketAddress(*details.options, address);
|
542
|
-
string filename = parseUnixSocketAddress(address);
|
543
|
-
|
544
|
-
// Verify that the socket filename is absolute.
|
545
|
-
if (!isAbsolutePath(filename)) {
|
546
|
-
error << "It reported a non-absolute socket filename: \"" <<
|
547
|
-
cEscapeString(filename) << "\"";
|
548
|
-
break;
|
549
|
-
}
|
550
|
-
|
551
|
-
// Verify that the process owns the socket.
|
552
|
-
struct stat buf;
|
553
|
-
if (lstat(filename.c_str(), &buf) == -1) {
|
554
|
-
int e = errno;
|
555
|
-
error << "It reported an inaccessible socket filename: \"" <<
|
556
|
-
cEscapeString(filename) << "\" (lstat() failed with errno " <<
|
557
|
-
e << ": " << strerror(e) << ")";
|
558
|
-
break;
|
559
|
-
}
|
560
|
-
if (buf.st_uid != details.preparation->userSwitching.uid) {
|
561
|
-
error << "It advertised a Unix domain socket that has a different " <<
|
562
|
-
"owner than expected (should be UID " << details.preparation->userSwitching.uid <<
|
563
|
-
", but actual UID was " << buf.st_uid << ")";
|
564
|
-
break;
|
565
|
-
}
|
566
|
-
break;
|
567
|
-
}
|
568
|
-
case SAT_TCP:
|
569
|
-
// TODO: validate that the socket is localhost.
|
570
|
-
break;
|
571
|
-
default:
|
572
|
-
error << "It reported an unsupported socket address type: \"" <<
|
573
|
-
cEscapeString(address) << "\"";
|
574
|
-
break;
|
575
|
-
}
|
576
|
-
|
577
|
-
return error.str();
|
578
|
-
}
|
579
|
-
|
580
|
-
static void checkChrootDirectories(const Options &options) {
|
581
|
-
if (!options.preexecChroot.empty()) {
|
582
|
-
// TODO: check whether appRoot is a child directory of preexecChroot
|
583
|
-
// and whether postexecChroot is a child directory of appRoot.
|
584
|
-
}
|
585
|
-
}
|
586
|
-
|
587
|
-
static void createCommandArgs(const vector<string> &command,
|
588
|
-
shared_array<const char *> &args)
|
79
|
+
void setConfigFromAppPoolOptions(Config *config, Json::Value &extraArgs,
|
80
|
+
const AppPoolOptions &options)
|
589
81
|
{
|
590
|
-
|
591
|
-
for (unsigned int i = 1; i < command.size(); i++) {
|
592
|
-
args[i - 1] = command[i].c_str();
|
593
|
-
}
|
594
|
-
args[command.size() - 1] = NULL;
|
595
|
-
}
|
596
|
-
|
597
|
-
void possiblyRaiseInternalError(const Options &options) {
|
598
|
-
if (options.raiseInternalError) {
|
599
|
-
throw RuntimeException("An internal error!");
|
600
|
-
}
|
601
|
-
}
|
602
|
-
|
603
|
-
void throwAppSpawnException(const string &msg,
|
604
|
-
SpawnException::ErrorKind errorKind,
|
605
|
-
NegotiationDetails &details)
|
606
|
-
{
|
607
|
-
TRACE_POINT();
|
608
|
-
// Stop the stderr capturing thread and get the captured stderr
|
609
|
-
// output so far.
|
610
|
-
string stderrOutput;
|
611
|
-
if (details.stderrCapturer != NULL) {
|
612
|
-
stderrOutput = details.stderrCapturer->stop();
|
613
|
-
}
|
614
|
-
|
615
|
-
// If the exception wasn't due to a timeout, try to capture the
|
616
|
-
// remaining stderr output for at most 2 seconds.
|
617
|
-
if (errorKind != SpawnException::PRELOADER_STARTUP_TIMEOUT
|
618
|
-
&& errorKind != SpawnException::APP_STARTUP_TIMEOUT
|
619
|
-
&& details.stderrCapturer != NULL)
|
620
|
-
{
|
621
|
-
bool done = false;
|
622
|
-
unsigned long long timeout = 2000;
|
623
|
-
while (!done) {
|
624
|
-
char buf[1024 * 32];
|
625
|
-
unsigned int ret;
|
626
|
-
|
627
|
-
try {
|
628
|
-
ret = readExact(details.stderrCapturer->getFd(), buf,
|
629
|
-
sizeof(buf), &timeout);
|
630
|
-
if (ret == 0) {
|
631
|
-
done = true;
|
632
|
-
} else {
|
633
|
-
stderrOutput.append(buf, ret);
|
634
|
-
}
|
635
|
-
} catch (const SystemException &e) {
|
636
|
-
P_WARN("Stderr I/O capture error: " << e.what());
|
637
|
-
done = true;
|
638
|
-
} catch (const TimeoutException &) {
|
639
|
-
done = true;
|
640
|
-
}
|
641
|
-
}
|
642
|
-
}
|
643
|
-
details.stderrCapturer.reset();
|
644
|
-
|
645
|
-
// Now throw SpawnException with the captured stderr output
|
646
|
-
// as error response.
|
647
|
-
SpawnException e(msg,
|
648
|
-
createErrorPageFromStderrOutput(msg, errorKind, stderrOutput),
|
649
|
-
true,
|
650
|
-
errorKind);
|
651
|
-
annotateAppSpawnException(e, details);
|
652
|
-
throwSpawnException(e, *details.options);
|
653
|
-
}
|
654
|
-
|
655
|
-
void throwSpawnException(SpawnException &e, const Options &options) {
|
656
|
-
if (config->errorHandler != NULL) {
|
657
|
-
config->errorHandler(config, e, options);
|
658
|
-
}
|
659
|
-
throw e;
|
660
|
-
}
|
661
|
-
|
662
|
-
virtual void annotateAppSpawnException(SpawnException &e, NegotiationDetails &details) {
|
663
|
-
if (details.debugDir != NULL) {
|
664
|
-
e.addAnnotations(details.debugDir->readAll());
|
665
|
-
}
|
666
|
-
}
|
667
|
-
|
668
|
-
string createErrorPageFromStderrOutput(const string &msg,
|
669
|
-
SpawnException::ErrorKind errorKind,
|
670
|
-
const string &stderrOutput)
|
671
|
-
{
|
672
|
-
// These kinds of SpawnExceptions are not supposed to be handled through this function.
|
673
|
-
assert(errorKind != SpawnException::PRELOADER_STARTUP_EXPLAINABLE_ERROR);
|
674
|
-
assert(errorKind != SpawnException::APP_STARTUP_EXPLAINABLE_ERROR);
|
675
|
-
|
676
|
-
string result = escapeHTML(msg);
|
677
|
-
|
678
|
-
if (errorKind == SpawnException::PRELOADER_STARTUP_TIMEOUT
|
679
|
-
|| errorKind == SpawnException::APP_STARTUP_TIMEOUT
|
680
|
-
|| errorKind == SpawnException::PRELOADER_STARTUP_ERROR
|
681
|
-
|| errorKind == SpawnException::APP_STARTUP_ERROR)
|
682
|
-
{
|
683
|
-
result.append(" Please read <a href=\"https://github.com/phusion/passenger/wiki/Debugging-application-startup-problems\">this article</a> "
|
684
|
-
"for more information about this problem.");
|
685
|
-
}
|
686
|
-
result.append("<br>\n<h2>Raw process output:</h2>\n");
|
687
|
-
|
688
|
-
if (strip(stderrOutput).empty()) {
|
689
|
-
result.append("(empty)");
|
690
|
-
} else {
|
691
|
-
result.append("<pre>");
|
692
|
-
result.append(escapeHTML(stderrOutput));
|
693
|
-
result.append("</pre>");
|
694
|
-
}
|
695
|
-
|
696
|
-
return result;
|
697
|
-
}
|
698
|
-
|
699
|
-
template<typename Details>
|
700
|
-
string readMessageLine(Details &details) {
|
701
|
-
TRACE_POINT();
|
702
|
-
while (true) {
|
703
|
-
string result = details.io.readLine(1024 * 16, &details.timeout);
|
704
|
-
string line = result;
|
705
|
-
if (!line.empty() && line[line.size() - 1] == '\n') {
|
706
|
-
line.erase(line.size() - 1, 1);
|
707
|
-
}
|
708
|
-
|
709
|
-
if (result.empty()) {
|
710
|
-
// EOF
|
711
|
-
return result;
|
712
|
-
} else if (startsWith(result, "!> ")) {
|
713
|
-
P_DEBUG("[App " << details.pid << " stdout] " << line);
|
714
|
-
result.erase(0, sizeof("!> ") - 1);
|
715
|
-
return result;
|
716
|
-
} else {
|
717
|
-
if (details.stderrCapturer != NULL) {
|
718
|
-
details.stderrCapturer->appendToBuffer(result);
|
719
|
-
}
|
720
|
-
LoggingKit::logAppOutput(details.options->getAppGroupName(), details.pid, "stdout", line.data(), line.size(), details.options->appLogFile);
|
721
|
-
}
|
722
|
-
}
|
723
|
-
}
|
724
|
-
|
725
|
-
SpawnPreparationInfo prepareSpawn(const Options &options) {
|
726
|
-
TRACE_POINT();
|
727
|
-
SpawnPreparationInfo info;
|
728
|
-
prepareChroot(info, options);
|
729
|
-
info.userSwitching = prepareUserSwitching(options);
|
730
|
-
prepareSwitchingWorkingDirectory(info, options);
|
731
|
-
return info;
|
732
|
-
}
|
733
|
-
|
734
|
-
void prepareChroot(SpawnPreparationInfo &info, const Options &options) {
|
735
|
-
TRACE_POINT();
|
736
|
-
info.appRoot = absolutizePath(options.appRoot);
|
737
|
-
if (options.preexecChroot.empty()) {
|
738
|
-
info.chrootDir = "/";
|
739
|
-
} else {
|
740
|
-
info.chrootDir = absolutizePath(options.preexecChroot);
|
741
|
-
}
|
742
|
-
if (info.appRoot != info.chrootDir && startsWith(info.appRoot, info.chrootDir + "/")) {
|
743
|
-
SpawnException e("Invalid configuration: '" + info.chrootDir +
|
744
|
-
"' has been configured as the chroot jail, but the application " +
|
745
|
-
"root directory '" + info.appRoot + "' is not a subdirectory of the " +
|
746
|
-
"chroot directory, which it must be.");
|
747
|
-
throwSpawnException(e, options);
|
748
|
-
}
|
749
|
-
if (info.appRoot == info.chrootDir) {
|
750
|
-
info.appRootInsideChroot = "/";
|
751
|
-
} else if (info.chrootDir == "/") {
|
752
|
-
info.appRootInsideChroot = info.appRoot;
|
753
|
-
} else {
|
754
|
-
info.appRootInsideChroot = info.appRoot.substr(info.chrootDir.size());
|
755
|
-
}
|
756
|
-
}
|
757
|
-
|
758
|
-
void prepareSwitchingWorkingDirectory(SpawnPreparationInfo &info, const Options &options) const {
|
759
|
-
vector<string> components;
|
760
|
-
split(info.appRootInsideChroot, '/', components);
|
761
|
-
assert(components.front() == "");
|
762
|
-
components.erase(components.begin());
|
763
|
-
|
764
|
-
for (unsigned int i = 0; i < components.size(); i++) {
|
765
|
-
string path;
|
766
|
-
for (unsigned int j = 0; j <= i; j++) {
|
767
|
-
path.append("/");
|
768
|
-
path.append(components[j]);
|
769
|
-
}
|
770
|
-
if (path.empty()) {
|
771
|
-
path = "/";
|
772
|
-
}
|
773
|
-
if (info.chrootDir == "/") {
|
774
|
-
info.appRootPaths.push_back(path);
|
775
|
-
} else {
|
776
|
-
info.appRootPaths.push_back(info.chrootDir + path);
|
777
|
-
}
|
778
|
-
info.appRootPathsInsideChroot.push_back(path);
|
779
|
-
}
|
780
|
-
|
781
|
-
assert(info.appRootPathsInsideChroot.back() == info.appRootInsideChroot);
|
782
|
-
}
|
783
|
-
|
784
|
-
bool shouldLoadShellEnvvars(const Options &options, const SpawnPreparationInfo &preparation) const {
|
785
|
-
if (options.loadShellEnvvars) {
|
786
|
-
string shellName = extractBaseName(preparation.userSwitching.shell);
|
787
|
-
bool retVal = shellName == "bash" || shellName == "zsh" || shellName == "ksh";
|
788
|
-
P_DEBUG("shellName = '" << shellName << "' in [bash,zsh,ksh]: " << (retVal ? "true" : "false"));
|
789
|
-
return retVal;
|
790
|
-
} else {
|
791
|
-
P_DEBUG("options.loadShellEnvvars = false");
|
792
|
-
return false;
|
793
|
-
}
|
794
|
-
}
|
795
|
-
|
796
|
-
string serializeEnvvarsFromPoolOptions(const Options &options) const {
|
797
|
-
vector< pair<StaticString, StaticString> >::const_iterator it, end;
|
798
|
-
string result;
|
799
|
-
|
800
|
-
appendNullTerminatedKeyValue(result, "IN_PASSENGER", "1");
|
801
|
-
appendNullTerminatedKeyValue(result, "PYTHONUNBUFFERED", "1");
|
802
|
-
appendNullTerminatedKeyValue(result, "NODE_PATH", config->resourceLocator->getNodeLibDir());
|
803
|
-
appendNullTerminatedKeyValue(result, "RAILS_ENV", options.environment);
|
804
|
-
appendNullTerminatedKeyValue(result, "RACK_ENV", options.environment);
|
805
|
-
appendNullTerminatedKeyValue(result, "WSGI_ENV", options.environment);
|
806
|
-
appendNullTerminatedKeyValue(result, "NODE_ENV", options.environment);
|
807
|
-
appendNullTerminatedKeyValue(result, "PASSENGER_APP_ENV", options.environment);
|
808
|
-
if (!options.baseURI.empty() && options.baseURI != "/") {
|
809
|
-
appendNullTerminatedKeyValue(result,
|
810
|
-
"RAILS_RELATIVE_URL_ROOT",
|
811
|
-
options.baseURI);
|
812
|
-
appendNullTerminatedKeyValue(result,
|
813
|
-
"RACK_BASE_URI",
|
814
|
-
options.baseURI);
|
815
|
-
appendNullTerminatedKeyValue(result,
|
816
|
-
"PASSENGER_BASE_URI",
|
817
|
-
options.baseURI);
|
818
|
-
}
|
819
|
-
|
82
|
+
string startCommand = options.getStartCommand(*context->resourceLocator);
|
820
83
|
string envvarsData;
|
821
84
|
try {
|
822
85
|
envvarsData = modp::b64_decode(options.environmentVariables.data(),
|
@@ -827,376 +90,49 @@ protected:
|
|
827
90
|
envvarsData.clear();
|
828
91
|
}
|
829
92
|
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
string jail_err;
|
864
|
-
int rc = liblve.jail(pw, jail_err);
|
865
|
-
if (rc < 0)
|
866
|
-
{
|
867
|
-
printf("!> Error\n");
|
868
|
-
printf("!> \n");
|
869
|
-
printf("enterLve() failed: %s\n", jail_err.c_str());
|
870
|
-
fflush(stdout);
|
871
|
-
_exit(1);
|
872
|
-
}
|
873
|
-
}
|
874
|
-
|
875
|
-
void switchUser(const SpawnPreparationInfo &info) {
|
876
|
-
if (info.userSwitching.enabled) {
|
877
|
-
enterLveJail(&info.userSwitching.lveUserPwd);
|
878
|
-
|
879
|
-
bool setgroupsCalled = false;
|
880
|
-
#ifdef HAVE_GETGROUPLIST
|
881
|
-
if (info.userSwitching.ngroups <= NGROUPS_MAX) {
|
882
|
-
setgroupsCalled = true;
|
883
|
-
if (setgroups(info.userSwitching.ngroups,
|
884
|
-
info.userSwitching.gidset.get()) == -1)
|
885
|
-
{
|
886
|
-
int e = errno;
|
887
|
-
printf("!> Error\n");
|
888
|
-
printf("!> \n");
|
889
|
-
printf("setgroups(%d, ...) failed: %s (errno=%d)\n",
|
890
|
-
info.userSwitching.ngroups, strerror(e), e);
|
891
|
-
fflush(stdout);
|
892
|
-
_exit(1);
|
893
|
-
}
|
894
|
-
}
|
895
|
-
#endif
|
896
|
-
if (!setgroupsCalled && initgroups(info.userSwitching.username.c_str(),
|
897
|
-
info.userSwitching.gid) == -1)
|
898
|
-
{
|
899
|
-
int e = errno;
|
900
|
-
printf("!> Error\n");
|
901
|
-
printf("!> \n");
|
902
|
-
printf("initgroups() failed: %s (errno=%d)\n",
|
903
|
-
strerror(e), e);
|
904
|
-
fflush(stdout);
|
905
|
-
_exit(1);
|
906
|
-
}
|
907
|
-
if (setgid(info.userSwitching.gid) == -1) {
|
908
|
-
int e = errno;
|
909
|
-
printf("!> Error\n");
|
910
|
-
printf("!> \n");
|
911
|
-
printf("setgid() failed: %s (errno=%d)\n",
|
912
|
-
strerror(e), e);
|
913
|
-
fflush(stdout);
|
914
|
-
_exit(1);
|
915
|
-
}
|
916
|
-
if (setuid(info.userSwitching.uid) == -1) {
|
917
|
-
int e = errno;
|
918
|
-
printf("!> Error\n");
|
919
|
-
printf("!> \n");
|
920
|
-
printf("setuid() failed: %s (errno=%d)\n",
|
921
|
-
strerror(e), e);
|
922
|
-
fflush(stdout);
|
923
|
-
_exit(1);
|
924
|
-
}
|
925
|
-
|
926
|
-
// We set these environment variables here instead of
|
927
|
-
// in the SpawnPreparer because SpawnPreparer might
|
928
|
-
// be executed by bash, but these environment variables
|
929
|
-
// must be set before bash.
|
930
|
-
setenv("USER", info.userSwitching.username.c_str(), 1);
|
931
|
-
setenv("LOGNAME", info.userSwitching.username.c_str(), 1);
|
932
|
-
setenv("SHELL", info.userSwitching.shell.c_str(), 1);
|
933
|
-
setenv("HOME", info.userSwitching.home.c_str(), 1);
|
934
|
-
// The application root may contain one or more symlinks
|
935
|
-
// in its path. If the application calls getcwd(), it will
|
936
|
-
// get the resolved path.
|
937
|
-
//
|
938
|
-
// It turns out that there is no such thing as a path without
|
939
|
-
// unresolved symlinks. The shell presents a working directory with
|
940
|
-
// unresolved symlinks (which it calls the "logical working directory"),
|
941
|
-
// but that is an illusion provided by the shell. The shell reports
|
942
|
-
// the logical working directory though the PWD environment variable.
|
943
|
-
//
|
944
|
-
// See also:
|
945
|
-
// https://github.com/phusion/passenger/issues/1596#issuecomment-138154045
|
946
|
-
// http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pwd.c
|
947
|
-
// http://www.opensource.apple.com/source/shell_cmds/shell_cmds-170/pwd/pwd.c
|
948
|
-
setenv("PWD", info.appRoot.c_str(), 1);
|
949
|
-
}
|
93
|
+
config->appGroupName = options.getAppGroupName();
|
94
|
+
config->appRoot = options.appRoot;
|
95
|
+
config->logLevel = options.logLevel;
|
96
|
+
config->genericApp = false;
|
97
|
+
config->startsUsingWrapper = true;
|
98
|
+
config->wrapperSuppliedByThirdParty = false;
|
99
|
+
config->findFreePort = false;
|
100
|
+
config->loadShellEnvvars = options.loadShellEnvvars;
|
101
|
+
config->startCommand = startCommand;
|
102
|
+
config->startupFile = options.getStartupFile();
|
103
|
+
config->appType = options.appType;
|
104
|
+
config->appEnv = options.environment;
|
105
|
+
config->baseURI = options.baseURI;
|
106
|
+
config->environmentVariables = decodeEnvironmentVariables(
|
107
|
+
envvarsData);
|
108
|
+
config->logFile = options.appLogFile;
|
109
|
+
config->apiKey = options.apiKey;
|
110
|
+
config->groupUuid = options.groupUuid;
|
111
|
+
config->lveMinUid = options.lveMinUid;
|
112
|
+
config->fileDescriptorUlimit = options.fileDescriptorUlimit;
|
113
|
+
config->startTimeoutMsec = options.startTimeout;
|
114
|
+
|
115
|
+
UserSwitchingInfo info = prepareUserSwitching(options);
|
116
|
+
config->user = info.username;
|
117
|
+
config->group = info.groupname;
|
118
|
+
|
119
|
+
extraArgs["spawn_method"] = options.spawnMethod.toString();
|
120
|
+
|
121
|
+
/******************/
|
122
|
+
/******************/
|
123
|
+
|
124
|
+
config->internStrings();
|
950
125
|
}
|
951
126
|
|
952
|
-
void
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
int e = errno;
|
957
|
-
fprintf(stderr, "Cannot chroot() to '%s': %s (errno=%d)\n",
|
958
|
-
info.chrootDir.c_str(),
|
959
|
-
strerror(e),
|
960
|
-
e);
|
961
|
-
fflush(stderr);
|
962
|
-
_exit(1);
|
963
|
-
}
|
964
|
-
|
965
|
-
ret = chdir("/");
|
966
|
-
if (ret == -1) {
|
967
|
-
int e = errno;
|
968
|
-
fprintf(stderr, "Cannot chdir(\"/\") inside chroot: %s (errno=%d)\n",
|
969
|
-
strerror(e),
|
970
|
-
e);
|
971
|
-
fflush(stderr);
|
972
|
-
_exit(1);
|
973
|
-
}
|
974
|
-
}
|
975
|
-
}
|
976
|
-
|
977
|
-
void setUlimits(const Options &options) {
|
978
|
-
if (options.fileDescriptorUlimit != 0) {
|
979
|
-
struct rlimit limit;
|
980
|
-
int ret;
|
981
|
-
|
982
|
-
limit.rlim_cur = options.fileDescriptorUlimit;
|
983
|
-
limit.rlim_max = options.fileDescriptorUlimit;
|
984
|
-
do {
|
985
|
-
ret = setrlimit(RLIMIT_NOFILE, &limit);
|
986
|
-
} while (ret == -1 && errno == EINTR);
|
987
|
-
|
988
|
-
if (ret == -1) {
|
989
|
-
int e = errno;
|
990
|
-
fprintf(stderr, "Unable to set file descriptor ulimit to %u: %s (errno=%d)",
|
991
|
-
options.fileDescriptorUlimit, strerror(e), e);
|
992
|
-
fflush(stderr);
|
993
|
-
}
|
994
|
-
}
|
995
|
-
}
|
996
|
-
|
997
|
-
void setWorkingDirectory(const SpawnPreparationInfo &info) {
|
998
|
-
vector<string>::const_iterator it, end = info.appRootPathsInsideChroot.end();
|
999
|
-
int ret;
|
1000
|
-
|
1001
|
-
for (it = info.appRootPathsInsideChroot.begin(); it != end; it++) {
|
1002
|
-
struct stat buf;
|
1003
|
-
ret = stat(it->c_str(), &buf);
|
1004
|
-
if (ret == -1 && errno == EACCES) {
|
1005
|
-
char parent[PATH_MAX];
|
1006
|
-
const char *end = strrchr(it->c_str(), '/');
|
1007
|
-
memcpy(parent, it->c_str(), end - it->c_str());
|
1008
|
-
parent[end - it->c_str()] = '\0';
|
1009
|
-
|
1010
|
-
printf("!> Error\n");
|
1011
|
-
printf("!> \n");
|
1012
|
-
printf("This web application process is being run as user '%s' and group '%s' "
|
1013
|
-
"and must be able to access its application root directory '%s'. "
|
1014
|
-
"However, the parent directory '%s' has wrong permissions, thereby "
|
1015
|
-
"preventing this process from accessing its application root directory. "
|
1016
|
-
"Please fix the permissions of the directory '%s' first.\n",
|
1017
|
-
info.userSwitching.username.c_str(),
|
1018
|
-
info.userSwitching.groupname.c_str(),
|
1019
|
-
info.appRootPaths.back().c_str(),
|
1020
|
-
parent,
|
1021
|
-
parent);
|
1022
|
-
fflush(stdout);
|
1023
|
-
_exit(1);
|
1024
|
-
} else if (ret == -1) {
|
1025
|
-
int e = errno;
|
1026
|
-
printf("!> Error\n");
|
1027
|
-
printf("!> \n");
|
1028
|
-
printf("Unable to stat() directory '%s': %s (errno=%d)\n",
|
1029
|
-
it->c_str(), strerror(e), e);
|
1030
|
-
fflush(stdout);
|
1031
|
-
_exit(1);
|
1032
|
-
}
|
1033
|
-
}
|
1034
|
-
|
1035
|
-
ret = chdir(info.appRootPathsInsideChroot.back().c_str());
|
1036
|
-
if (ret == 0) {
|
1037
|
-
setenv("PWD", info.appRootPathsInsideChroot.back().c_str(), 1);
|
1038
|
-
} else if (ret == -1 && errno == EACCES) {
|
1039
|
-
printf("!> Error\n");
|
1040
|
-
printf("!> \n");
|
1041
|
-
printf("This web application process is being run as user '%s' and group '%s' "
|
1042
|
-
"and must be able to access its application root directory '%s'. "
|
1043
|
-
"However this directory is not accessible because it has wrong permissions. "
|
1044
|
-
"Please fix these permissions first.\n",
|
1045
|
-
info.userSwitching.username.c_str(),
|
1046
|
-
info.userSwitching.groupname.c_str(),
|
1047
|
-
info.appRootPaths.back().c_str());
|
1048
|
-
fflush(stdout);
|
1049
|
-
_exit(1);
|
1050
|
-
} else {
|
1051
|
-
int e = errno;
|
1052
|
-
printf("!> Error\n");
|
1053
|
-
printf("!> \n");
|
1054
|
-
printf("Unable to change working directory to '%s': %s (errno=%d)\n",
|
1055
|
-
info.appRootPathsInsideChroot.back().c_str(), strerror(e), e);
|
1056
|
-
fflush(stdout);
|
1057
|
-
_exit(1);
|
1058
|
-
}
|
1059
|
-
}
|
1060
|
-
|
1061
|
-
/**
|
1062
|
-
* Execute the process spawning negotiation protocol.
|
1063
|
-
*/
|
1064
|
-
Result negotiateSpawn(NegotiationDetails &details) {
|
1065
|
-
TRACE_POINT();
|
1066
|
-
details.spawnStartTime = SystemTime::getUsec();
|
1067
|
-
details.gupid = integerToHex(SystemTime::get() / 60) + "-" +
|
1068
|
-
config->randomGenerator->generateAsciiString(10);
|
1069
|
-
details.timeout = details.options->startTimeout * 1000;
|
1070
|
-
|
1071
|
-
string result;
|
1072
|
-
try {
|
1073
|
-
result = readMessageLine(details);
|
1074
|
-
} catch (const SystemException &e) {
|
1075
|
-
throwAppSpawnException("An error occurred while starting the "
|
1076
|
-
"web application. There was an I/O error while reading its "
|
1077
|
-
"handshake message: " + e.sys(),
|
1078
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1079
|
-
details);
|
1080
|
-
} catch (const TimeoutException &) {
|
1081
|
-
throwAppSpawnException("An error occurred while starting the "
|
1082
|
-
"web application: it did not write a handshake message in time.",
|
1083
|
-
SpawnException::APP_STARTUP_TIMEOUT,
|
1084
|
-
details);
|
1085
|
-
}
|
1086
|
-
|
1087
|
-
protocol_begin:
|
1088
|
-
if (result == "I have control 1.0\n") {
|
1089
|
-
UPDATE_TRACE_POINT();
|
1090
|
-
sendSpawnRequest(details);
|
1091
|
-
try {
|
1092
|
-
result = readMessageLine(details);
|
1093
|
-
} catch (const SystemException &e) {
|
1094
|
-
throwAppSpawnException("An error occurred while starting the "
|
1095
|
-
"web application. There was an I/O error while reading its "
|
1096
|
-
"startup response: " + e.sys(),
|
1097
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1098
|
-
details);
|
1099
|
-
} catch (const TimeoutException &) {
|
1100
|
-
throwAppSpawnException("An error occurred while starting the "
|
1101
|
-
"web application: it did not write a startup response in time. "
|
1102
|
-
"If your app needs more time to start you can increase the "
|
1103
|
-
"Passenger start timeout config option.",
|
1104
|
-
SpawnException::APP_STARTUP_TIMEOUT,
|
1105
|
-
details);
|
1106
|
-
}
|
1107
|
-
if (result == "Ready\n") {
|
1108
|
-
return handleSpawnResponse(details);
|
1109
|
-
} else if (result == "Error\n") {
|
1110
|
-
handleSpawnErrorResponse(details);
|
1111
|
-
} else if (result == "I have control 1.0\n") {
|
1112
|
-
goto protocol_begin;
|
1113
|
-
} else {
|
1114
|
-
handleInvalidSpawnResponseType(result, details);
|
1115
|
-
}
|
1116
|
-
} else {
|
1117
|
-
UPDATE_TRACE_POINT();
|
1118
|
-
if (result == "Error\n") {
|
1119
|
-
handleSpawnErrorResponse(details);
|
1120
|
-
} else {
|
1121
|
-
handleInvalidSpawnResponseType(result, details);
|
1122
|
-
}
|
1123
|
-
}
|
1124
|
-
return Result(); // Never reached.
|
1125
|
-
}
|
1126
|
-
|
1127
|
-
void handleSpawnErrorResponse(NegotiationDetails &details) {
|
1128
|
-
TRACE_POINT();
|
1129
|
-
map<string, string> attributes;
|
1130
|
-
|
1131
|
-
while (true) {
|
1132
|
-
string line = readMessageLine(details);
|
1133
|
-
if (line.empty()) {
|
1134
|
-
throwAppSpawnException("An error occurred while starting the "
|
1135
|
-
"web application. It unexpected closed the connection while "
|
1136
|
-
"sending its startup response.",
|
1137
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1138
|
-
details);
|
1139
|
-
} else if (line[line.size() - 1] != '\n') {
|
1140
|
-
throwAppSpawnException("An error occurred while starting the "
|
1141
|
-
"web application. It sent a line without a newline character "
|
1142
|
-
"in its startup response.",
|
1143
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1144
|
-
details);
|
1145
|
-
} else if (line == "\n") {
|
1146
|
-
break;
|
1147
|
-
}
|
1148
|
-
|
1149
|
-
string::size_type pos = line.find(": ");
|
1150
|
-
if (pos == string::npos) {
|
1151
|
-
throwAppSpawnException("An error occurred while starting the "
|
1152
|
-
"web application. It sent a startup response line without "
|
1153
|
-
"separator.",
|
1154
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1155
|
-
details);
|
1156
|
-
}
|
1157
|
-
|
1158
|
-
string key = line.substr(0, pos);
|
1159
|
-
string value = line.substr(pos + 2, line.size() - pos - 3);
|
1160
|
-
attributes[key] = value;
|
1161
|
-
}
|
1162
|
-
|
1163
|
-
try {
|
1164
|
-
string message = details.io.readAll(&details.timeout);
|
1165
|
-
SpawnException e("An error occured while starting the web application.",
|
1166
|
-
message,
|
1167
|
-
attributes["html"] == "true",
|
1168
|
-
SpawnException::APP_STARTUP_EXPLAINABLE_ERROR);
|
1169
|
-
annotateAppSpawnException(e, details);
|
1170
|
-
throwSpawnException(e, *details.options);
|
1171
|
-
} catch (const SystemException &e) {
|
1172
|
-
throwAppSpawnException("An error occurred while starting the "
|
1173
|
-
"web application. It tried to report an error message, but "
|
1174
|
-
"an I/O error occurred while reading this error message: " +
|
1175
|
-
e.sys(),
|
1176
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1177
|
-
details);
|
1178
|
-
} catch (const TimeoutException &) {
|
1179
|
-
throwAppSpawnException("An error occurred while starting the "
|
1180
|
-
"web application. It tried to report an error message, but "
|
1181
|
-
"it took too much time doing that.",
|
1182
|
-
SpawnException::APP_STARTUP_TIMEOUT,
|
1183
|
-
details);
|
1184
|
-
}
|
127
|
+
static void nonInterruptableKillAndWaitpid(pid_t pid) {
|
128
|
+
boost::this_thread::disable_syscall_interruption dsi;
|
129
|
+
syscalls::kill(pid, SIGKILL);
|
130
|
+
syscalls::waitpid(pid, NULL, 0);
|
1185
131
|
}
|
1186
132
|
|
1187
|
-
void
|
1188
|
-
if (
|
1189
|
-
|
1190
|
-
"the web application. It exited before signalling successful "
|
1191
|
-
"startup back to " PROGRAM_NAME ".",
|
1192
|
-
SpawnException::APP_STARTUP_ERROR,
|
1193
|
-
details);
|
1194
|
-
} else {
|
1195
|
-
throwAppSpawnException("An error occurred while starting "
|
1196
|
-
"the web application. It sent an unknown response type \"" +
|
1197
|
-
cEscapeString(line) + "\".",
|
1198
|
-
SpawnException::APP_STARTUP_PROTOCOL_ERROR,
|
1199
|
-
details);
|
133
|
+
static void possiblyRaiseInternalError(const AppPoolOptions &options) {
|
134
|
+
if (options.raiseInternalError) {
|
135
|
+
throw RuntimeException("An internal error!");
|
1200
136
|
}
|
1201
137
|
}
|
1202
138
|
|
@@ -1206,14 +142,14 @@ public:
|
|
1206
142
|
*/
|
1207
143
|
const unsigned long long creationTime;
|
1208
144
|
|
1209
|
-
Spawner(
|
1210
|
-
:
|
145
|
+
Spawner(Context *_context)
|
146
|
+
: context(_context),
|
1211
147
|
creationTime(SystemTime::getUsec())
|
1212
148
|
{ }
|
1213
149
|
|
1214
150
|
virtual ~Spawner() { }
|
1215
151
|
|
1216
|
-
virtual Result spawn(const
|
152
|
+
virtual Result spawn(const AppPoolOptions &options) = 0;
|
1217
153
|
|
1218
154
|
virtual bool cleanable() const {
|
1219
155
|
return false;
|
@@ -1227,8 +163,8 @@ public:
|
|
1227
163
|
return 0;
|
1228
164
|
}
|
1229
165
|
|
1230
|
-
|
1231
|
-
return
|
166
|
+
Context *getContext() const {
|
167
|
+
return context;
|
1232
168
|
}
|
1233
169
|
};
|
1234
170
|
typedef boost::shared_ptr<Spawner> SpawnerPtr;
|