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.

Files changed (241) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +13 -0
  3. data/CONTRIBUTORS +5 -1
  4. data/build/agent.rb +22 -2
  5. data/build/cxx_tests.rb +41 -5
  6. data/build/misc.rb +4 -1
  7. data/build/support/cxx_dependency_map.rb +1746 -908
  8. data/build/support/vendor/cxx_hinted_parser/CxxHintedParser.sublime-project +8 -0
  9. data/build/support/vendor/cxx_hinted_parser/Gemfile +5 -0
  10. data/build/support/vendor/cxx_hinted_parser/Gemfile.lock +30 -0
  11. data/{src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core → build/support/vendor/cxx_hinted_parser}/LICENSE.md +1 -1
  12. data/build/support/vendor/cxx_hinted_parser/README.md +95 -0
  13. data/build/support/vendor/cxx_hinted_parser/Rakefile +4 -0
  14. 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
  15. data/build/support/vendor/cxx_hinted_parser/lib/cxx_hinted_parser/parser.rb +239 -0
  16. data/dev/ci/README.md +15 -2
  17. data/dev/ci/lib/set-container-envvars.sh +6 -0
  18. data/dev/ci/lib/setup-container.sh +4 -1
  19. data/dev/ci/scripts/debug-console-wrapper.sh +3 -1
  20. data/dev/ci/setup-host +5 -0
  21. data/dev/ci/tests/binaries/Jenkinsfile +105 -0
  22. data/dev/ci/tests/binaries/build-linux +38 -0
  23. data/dev/ci/tests/binaries/build-macos +40 -0
  24. data/dev/ci/tests/binaries/prepare-macos +38 -0
  25. data/dev/ci/tests/binaries/test-linux +45 -0
  26. data/dev/ci/tests/binaries/test-macos +38 -0
  27. data/dev/ci/tests/debian/Jenkinsfile +2 -2
  28. data/dev/ci/tests/rpm/Jenkinsfile +1 -1
  29. data/dev/configkit-schemas/index.json +3 -24
  30. data/dev/vagrant/nginx_rakefile +0 -1
  31. data/package.json +15 -5
  32. data/resources/templates/error_renderer/.editorconfig +19 -0
  33. data/resources/templates/error_renderer/with_details/README.md +9 -0
  34. data/resources/templates/error_renderer/with_details/dist/bundle.js +33 -0
  35. data/resources/templates/error_renderer/with_details/dist/styles.css +17 -0
  36. data/resources/templates/error_renderer/with_details/src/DetailsView.jsx +52 -0
  37. data/resources/templates/error_renderer/with_details/src/GetHelpView.jsx +61 -0
  38. data/resources/templates/error_renderer/with_details/src/JourneyView.css +50 -0
  39. data/resources/templates/error_renderer/with_details/src/JourneyView.jsx +621 -0
  40. data/resources/templates/error_renderer/with_details/src/PageMain.css +114 -0
  41. data/resources/templates/error_renderer/with_details/src/PageMain.jsx +136 -0
  42. data/resources/templates/error_renderer/with_details/src/ProblemDescriptionView.jsx +14 -0
  43. data/resources/templates/error_renderer/with_details/src/ProcessDetailsView.jsx +56 -0
  44. data/resources/templates/error_renderer/with_details/src/SolutionDescriptionView.css +5 -0
  45. data/resources/templates/error_renderer/with_details/src/SolutionDescriptionView.jsx +15 -0
  46. data/resources/templates/error_renderer/with_details/src/SummaryView.jsx +35 -0
  47. data/resources/templates/error_renderer/with_details/src/SystemComponentView.css +34 -0
  48. data/resources/templates/error_renderer/with_details/src/SystemComponentView.jsx +168 -0
  49. data/resources/templates/error_renderer/with_details/src/SystemComponentsView.css +13 -0
  50. data/resources/templates/error_renderer/with_details/src/SystemComponentsView.jsx +116 -0
  51. data/resources/templates/error_renderer/with_details/src/Tab.jsx +12 -0
  52. data/resources/templates/error_renderer/with_details/src/Tabs.jsx +104 -0
  53. data/resources/templates/error_renderer/with_details/src/bootstrap/bootstrap.css +3446 -0
  54. data/resources/templates/error_renderer/with_details/src/bootstrap/bootstrap.js +293 -0
  55. data/resources/templates/error_renderer/with_details/src/bootstrap/config.json +401 -0
  56. data/resources/templates/error_renderer/with_details/src/index.html.template +22 -0
  57. data/resources/templates/error_renderer/with_details/src/index.jsx +23 -0
  58. data/resources/templates/error_renderer/with_details/webpack.config.js +47 -0
  59. data/resources/templates/error_renderer/without_details/dist/bundle.js +1 -0
  60. data/resources/templates/error_renderer/without_details/dist/styles.css +1 -0
  61. data/resources/templates/{undisclosed_error.html.template → error_renderer/without_details/src/index.html.template} +7 -11
  62. data/resources/templates/error_renderer/without_details/src/index.js +1 -0
  63. data/resources/templates/{error_layout.css → error_renderer/without_details/src/main.css} +5 -2
  64. data/resources/templates/error_renderer/without_details/webpack.config.js +42 -0
  65. data/src/agent/AgentMain.cpp +3 -3
  66. data/src/agent/Core/ApplicationPool/BasicProcessInfo.h +13 -0
  67. data/src/agent/Core/ApplicationPool/Common.h +3 -4
  68. data/src/agent/Core/ApplicationPool/Context.h +27 -17
  69. data/src/agent/Core/ApplicationPool/Group.h +3 -1
  70. data/src/agent/Core/ApplicationPool/Group/InitializationAndShutdown.cpp +2 -12
  71. data/src/agent/Core/ApplicationPool/Group/InternalUtils.cpp +55 -10
  72. data/src/agent/Core/ApplicationPool/Group/LifetimeAndBasics.cpp +1 -1
  73. data/src/agent/Core/ApplicationPool/Group/OutOfBandWork.cpp +1 -1
  74. data/src/agent/Core/ApplicationPool/Group/SpawningAndRestarting.cpp +13 -6
  75. data/src/agent/Core/ApplicationPool/Implementation.cpp +16 -100
  76. data/src/agent/Core/ApplicationPool/Options.h +8 -65
  77. data/src/agent/Core/ApplicationPool/Pool.h +4 -21
  78. data/src/agent/Core/ApplicationPool/Pool/AnalyticsCollection.cpp +1 -60
  79. data/src/agent/Core/ApplicationPool/Pool/GeneralUtils.cpp +10 -13
  80. data/src/agent/Core/ApplicationPool/Pool/InitializationAndShutdown.cpp +3 -8
  81. data/src/agent/Core/ApplicationPool/Pool/Miscellaneous.cpp +2 -34
  82. data/src/agent/Core/ApplicationPool/Pool/StateInspection.cpp +1 -1
  83. data/src/agent/Core/ApplicationPool/Process.cpp +17 -12
  84. data/src/agent/Core/ApplicationPool/Process.h +146 -93
  85. data/src/agent/Core/ApplicationPool/Session.h +2 -2
  86. data/src/agent/Core/ApplicationPool/Socket.h +28 -27
  87. data/src/agent/Core/Config.h +1 -3
  88. data/src/agent/Core/ConfigChange.cpp +2 -4
  89. data/src/agent/Core/Controller.h +2 -8
  90. data/src/agent/Core/Controller/BufferBody.cpp +0 -2
  91. data/src/agent/Core/Controller/CheckoutSession.cpp +12 -24
  92. data/src/agent/Core/Controller/Config.h +1 -9
  93. data/src/agent/Core/Controller/ForwardResponse.cpp +0 -34
  94. data/src/agent/Core/Controller/Hooks.cpp +0 -7
  95. data/src/agent/Core/Controller/InitRequest.cpp +0 -43
  96. data/src/agent/Core/Controller/InitializationAndShutdown.cpp +0 -4
  97. data/src/agent/Core/Controller/Request.h +1 -35
  98. data/src/agent/Core/Controller/SendRequest.cpp +0 -32
  99. data/src/agent/Core/CoreMain.cpp +19 -32
  100. data/src/agent/Core/SpawningKit/Config.h +329 -55
  101. data/src/agent/Core/SpawningKit/Config/AutoGeneratedCode.h +369 -0
  102. data/src/agent/Core/SpawningKit/Config/AutoGeneratedCode.h.cxxcodebuilder +307 -0
  103. data/src/agent/Core/SpawningKit/Context.h +211 -0
  104. data/src/agent/Core/SpawningKit/DirectSpawner.h +112 -122
  105. data/src/agent/Core/SpawningKit/DummySpawner.h +59 -20
  106. data/src/agent/Core/SpawningKit/ErrorRenderer.h +117 -0
  107. data/src/agent/Core/SpawningKit/Exceptions.h +1157 -0
  108. data/src/agent/Core/SpawningKit/Factory.h +24 -17
  109. data/src/agent/Core/SpawningKit/{BackgroundIOCapturer.h → Handshake/BackgroundIOCapturer.h} +48 -18
  110. data/src/agent/Core/SpawningKit/Handshake/Perform.h +1650 -0
  111. data/src/agent/Core/SpawningKit/Handshake/Prepare.h +582 -0
  112. data/src/agent/Core/SpawningKit/Handshake/Session.h +91 -0
  113. data/src/agent/Core/SpawningKit/Handshake/WorkDir.h +100 -0
  114. data/src/agent/Core/SpawningKit/Journey.h +561 -0
  115. data/src/agent/Core/SpawningKit/PipeWatcher.h +41 -18
  116. data/src/agent/Core/SpawningKit/README.md +534 -0
  117. data/src/agent/Core/SpawningKit/Result.h +182 -7
  118. data/src/agent/Core/SpawningKit/Result/AutoGeneratedCode.h +69 -0
  119. data/src/agent/Core/SpawningKit/Result/AutoGeneratedCode.h.cxxcodebuilder +110 -0
  120. data/src/agent/Core/SpawningKit/SmartSpawner.h +1027 -562
  121. data/src/agent/Core/SpawningKit/Spawner.h +70 -1134
  122. data/src/agent/Core/SpawningKit/UserSwitchingRules.h +3 -33
  123. data/src/agent/README.md +2 -3
  124. data/src/agent/Shared/ApiServerUtils.h +2 -3
  125. data/src/agent/SpawnEnvSetupper/SpawnEnvSetupperMain.cpp +932 -0
  126. data/src/agent/Watchdog/Config.h +1 -3
  127. data/src/agent/Watchdog/WatchdogMain.cpp +2 -1
  128. data/src/apache2_module/ConfigGeneral/AutoGeneratedDefinitions.cpp +5 -0
  129. data/src/apache2_module/ConfigGeneral/AutoGeneratedManifestDefaultsInitialization.cpp +5 -0
  130. data/src/apache2_module/ConfigGeneral/ManifestGeneration.h +22 -13
  131. data/src/apache2_module/DirConfig/AutoGeneratedCreateFunction.cpp +5 -0
  132. data/src/apache2_module/DirConfig/AutoGeneratedHeaderSerialization.cpp +3 -0
  133. data/src/apache2_module/DirConfig/AutoGeneratedManifestGeneration.cpp +13 -0
  134. data/src/apache2_module/DirConfig/AutoGeneratedMergeFunction.cpp +7 -0
  135. data/src/apache2_module/DirConfig/AutoGeneratedStruct.h +13 -0
  136. data/src/cxx_supportlib/Constants.h +3 -1
  137. data/src/cxx_supportlib/Exceptions.h +0 -121
  138. data/src/cxx_supportlib/LoggingKit/Implementation.cpp +7 -6
  139. data/src/cxx_supportlib/LoggingKit/Logging.h +3 -1
  140. data/src/cxx_supportlib/Utils.cpp +42 -0
  141. data/src/cxx_supportlib/Utils.h +7 -0
  142. data/src/cxx_supportlib/Utils/IOUtils.cpp +58 -0
  143. data/src/cxx_supportlib/Utils/IOUtils.h +13 -0
  144. data/src/cxx_supportlib/Utils/JsonUtils.h +130 -23
  145. data/src/cxx_supportlib/Utils/ScopeGuard.h +9 -4
  146. data/src/cxx_supportlib/Utils/StrIntUtils.cpp +7 -0
  147. data/src/cxx_supportlib/Utils/StrIntUtils.h +1 -0
  148. data/src/cxx_supportlib/Utils/SystemTime.h +1 -1
  149. data/src/cxx_supportlib/Utils/Timer.h +1 -1
  150. data/src/cxx_supportlib/WebSocketCommandReverseServer.h +6 -4
  151. data/src/cxx_supportlib/vendor-copy/adhoc_lve.h +1 -0
  152. data/src/helper-scripts/node-loader.js +54 -59
  153. data/src/helper-scripts/rack-loader.rb +63 -60
  154. data/src/helper-scripts/rack-preloader.rb +125 -72
  155. data/src/helper-scripts/wsgi-loader.py +100 -43
  156. data/src/nginx_module/ConfigGeneral/AutoGeneratedDefinitions.c +120 -112
  157. data/src/nginx_module/ConfigGeneral/AutoGeneratedManifestDefaultsInitialization.c +15 -8
  158. data/src/nginx_module/ConfigGeneral/AutoGeneratedSetterFuncs.c +142 -142
  159. data/src/nginx_module/ConfigGeneral/ManifestGeneration.c +26 -15
  160. data/src/nginx_module/ConfigGeneral/ManifestGeneration.h +3 -0
  161. data/src/nginx_module/LocationConfig/AutoGeneratedCreateFunction.c +76 -70
  162. data/src/nginx_module/LocationConfig/AutoGeneratedHeaderSerialization.c +114 -99
  163. data/src/nginx_module/LocationConfig/AutoGeneratedManifestGeneration.c +170 -156
  164. data/src/nginx_module/LocationConfig/AutoGeneratedMergeFunction.c +38 -35
  165. data/src/nginx_module/LocationConfig/AutoGeneratedStruct.h +5 -1
  166. data/src/ruby_supportlib/phusion_passenger.rb +5 -5
  167. data/src/ruby_supportlib/phusion_passenger/admin_tools/instance.rb +14 -1
  168. data/src/ruby_supportlib/phusion_passenger/apache2/config_options.rb +8 -0
  169. data/src/ruby_supportlib/phusion_passenger/common_library.rb +0 -3
  170. data/src/ruby_supportlib/phusion_passenger/config/nginx_engine_compiler.rb +0 -1
  171. data/src/ruby_supportlib/phusion_passenger/constants.rb +2 -0
  172. data/src/ruby_supportlib/phusion_passenger/loader_shared_helpers.rb +646 -238
  173. data/src/ruby_supportlib/phusion_passenger/nginx/config_options.rb +117 -95
  174. data/src/ruby_supportlib/phusion_passenger/packaging.rb +0 -1
  175. data/src/ruby_supportlib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +5 -1
  176. data/src/ruby_supportlib/phusion_passenger/preloader_shared_helpers.rb +92 -69
  177. data/src/ruby_supportlib/phusion_passenger/public_api.rb +0 -17
  178. data/src/ruby_supportlib/phusion_passenger/rack/thread_handler_extension.rb +0 -3
  179. data/src/ruby_supportlib/phusion_passenger/request_handler.rb +4 -5
  180. data/src/ruby_supportlib/phusion_passenger/request_handler/thread_handler.rb +0 -22
  181. metadata +64 -67
  182. data/resources/templates/error_layout.html.template +0 -86
  183. data/resources/templates/general_error.html.template +0 -1
  184. data/resources/templates/general_error_with_html.html.template +0 -1
  185. data/src/agent/Core/ApplicationPool/ErrorRenderer.h +0 -131
  186. data/src/agent/Core/SpawningKit/Options.h +0 -41
  187. data/src/agent/Core/UnionStation/Connection.h +0 -173
  188. data/src/agent/Core/UnionStation/Context.h +0 -536
  189. data/src/agent/Core/UnionStation/StopwatchLog.h +0 -147
  190. data/src/agent/Core/UnionStation/Transaction.h +0 -249
  191. data/src/agent/SpawnPreparer/SpawnPreparerMain.cpp +0 -208
  192. data/src/cxx_supportlib/UnionStationFilterSupport.cpp +0 -67
  193. data/src/cxx_supportlib/UnionStationFilterSupport.h +0 -1622
  194. data/src/nodejs_supportlib/phusion_passenger/log_express.js +0 -106
  195. data/src/nodejs_supportlib/phusion_passenger/log_mongodb.js +0 -202
  196. data/src/nodejs_supportlib/phusion_passenger/ustreporter.js +0 -227
  197. data/src/nodejs_supportlib/phusion_passenger/ustrouter_connector.js +0 -448
  198. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/CONFIG.md +0 -37
  199. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/Gemfile +0 -17
  200. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/Gemfile.lock +0 -59
  201. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/README-API.md +0 -5
  202. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/README.md +0 -117
  203. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/Rakefile +0 -115
  204. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core.rb +0 -423
  205. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/api.rb +0 -238
  206. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/connection.rb +0 -67
  207. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/context.rb +0 -281
  208. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/lock.rb +0 -62
  209. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/log.rb +0 -66
  210. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/message_channel.rb +0 -157
  211. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter.rb +0 -150
  212. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/basics.rb +0 -199
  213. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/controllers.rb +0 -187
  214. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/misc.rb +0 -303
  215. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/view_rendering.rb +0 -91
  216. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/simple_json.rb +0 -396
  217. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/spec_helper.rb +0 -279
  218. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/time_point.rb +0 -39
  219. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/transaction.rb +0 -173
  220. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/utils.rb +0 -177
  221. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/version.rb +0 -32
  222. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/version_data.rb +0 -44
  223. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml.example +0 -16
  224. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml.travis +0 -20
  225. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml.travis-with-sudo +0 -18
  226. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/union_station_hooks_core.gemspec +0 -23
  227. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/Gemfile +0 -14
  228. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/Gemfile.lock +0 -45
  229. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/LICENSE.md +0 -19
  230. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/README.md +0 -104
  231. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/Rakefile +0 -160
  232. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails.rb +0 -200
  233. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/action_controller_extension.rb +0 -45
  234. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/action_view_subscriber.rb +0 -55
  235. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/active_record_subscriber.rb +0 -41
  236. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/active_support_benchmarkable_extension.rb +0 -47
  237. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/active_support_cache_subscriber.rb +0 -79
  238. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/exception_logger.rb +0 -57
  239. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/version.rb +0 -32
  240. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/version_data.rb +0 -44
  241. 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
- #include <oxt/backtrace.hpp>
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
- #include <FileDescriptor.h>
84
- #include <Exceptions.h>
85
- #include <StaticString.h>
86
- #include <Utils.h>
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
- * Appends key + "\0" + value + "\0" to 'output'.
266
- */
267
- static void appendNullTerminatedKeyValue(string &output, const StaticString &key,
268
- const StaticString &value)
269
- {
270
- output.append(key.data(), key.size());
271
- output.append(1, '\0');
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 pos = line.find(": ");
365
- if (pos == string::npos) {
366
- throwAppSpawnException("An error occurred while starting the "
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
- if (!hasSessionSockets(sockets)) {
430
- throwAppSpawnException("An error occured while starting the web "
431
- "application. It did not advertise any session sockets.",
432
- SpawnException::APP_STARTUP_PROTOCOL_ERROR,
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["type"] = "os_process";
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
- ConfigPtr config;
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
- bool isAbsolutePath(const StaticString &path) const {
514
- if (path.empty() || path[0] != '/') {
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
- args.reset(new const char *[command.size()]);
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
- if (!envvarsData.empty()) {
831
- // The envvars data is in the same format as `result`,
832
- // so we can just append it.
833
- result.append(envvarsData);
834
- }
835
-
836
- try {
837
- return modp::b64_encode(result);
838
- } catch (const std::runtime_error &) {
839
- throw RuntimeException("Unable to encode data into a base64 string");
840
- }
841
- }
842
-
843
- void enterLveJail(const struct passwd * pw) {
844
- if (!pw)
845
- return;
846
-
847
- string lve_init_err;
848
- adhoc_lve::LibLve& liblve = adhoc_lve::LveInitSignleton::getInstance(&lve_init_err);
849
- if (liblve.is_error())
850
- {
851
- printf("!> Error\n");
852
- printf("!> \n");
853
- printf("!> Failed to init LVE library%s%s\n",
854
- lve_init_err.empty()? "" : ": ",
855
- lve_init_err.c_str());
856
- fflush(stdout);
857
- _exit(1);
858
- }
859
-
860
- if (!liblve.is_lve_available())
861
- return;
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 setChroot(const SpawnPreparationInfo &info) {
953
- if (info.chrootDir != "/") {
954
- int ret = chroot(info.chrootDir.c_str());
955
- if (ret == -1) {
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 handleInvalidSpawnResponseType(const string &line, NegotiationDetails &details) {
1188
- if (line.empty()) {
1189
- throwAppSpawnException("An error occurred while starting "
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(const ConfigPtr &_config)
1210
- : config(_config),
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 Options &options) = 0;
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
- ConfigPtr getConfig() const {
1231
- return config;
166
+ Context *getContext() const {
167
+ return context;
1232
168
  }
1233
169
  };
1234
170
  typedef boost::shared_ptr<Spawner> SpawnerPtr;