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,7 +26,7 @@
26
26
  #ifndef _PASSENGER_SPAWNING_KIT_FACTORY_H_
27
27
  #define _PASSENGER_SPAWNING_KIT_FACTORY_H_
28
28
 
29
- #include <Core/SpawningKit/Spawner.h>
29
+ #include <Core/SpawningKit/Context.h>
30
30
  #include <Core/SpawningKit/SmartSpawner.h>
31
31
  #include <Core/SpawningKit/DirectSpawner.h>
32
32
  #include <Core/SpawningKit/DummySpawner.h>
@@ -42,11 +42,11 @@ using namespace oxt;
42
42
  class Factory {
43
43
  private:
44
44
  boost::mutex syncher;
45
- ConfigPtr config;
45
+ Context *context;
46
46
  DummySpawnerPtr dummySpawner;
47
47
 
48
- SpawnerPtr tryCreateSmartSpawner(const Options &options) {
49
- string dir = config->resourceLocator->getHelperScriptsDir();
48
+ SpawnerPtr tryCreateSmartSpawner(const AppPoolOptions &options) {
49
+ string dir = context->resourceLocator->getHelperScriptsDir();
50
50
  vector<string> preloaderCommand;
51
51
  if (options.appType == "rack") {
52
52
  preloaderCommand.push_back(options.ruby);
@@ -54,30 +54,37 @@ private:
54
54
  } else {
55
55
  return SpawnerPtr();
56
56
  }
57
- return boost::make_shared<SmartSpawner>(preloaderCommand,
58
- options, config);
57
+ return boost::make_shared<SmartSpawner>(context,
58
+ preloaderCommand, options);
59
59
  }
60
60
 
61
61
  public:
62
- Factory(const ConfigPtr &_config)
63
- : config(_config)
64
- { }
62
+ unsigned int spawnerCreationSleepTime;
63
+
64
+ Factory(Context *_context)
65
+ : context(_context),
66
+ spawnerCreationSleepTime(0)
67
+ {
68
+ if (context->debugSupport != NULL) {
69
+ spawnerCreationSleepTime = context->debugSupport->spawnerCreationSleepTime;
70
+ }
71
+ }
65
72
 
66
73
  virtual ~Factory() { }
67
74
 
68
- virtual SpawnerPtr create(const Options &options) {
75
+ virtual SpawnerPtr create(const AppPoolOptions &options) {
69
76
  if (options.spawnMethod == "smart" || options.spawnMethod == "smart-lv2") {
70
77
  SpawnerPtr spawner = tryCreateSmartSpawner(options);
71
78
  if (spawner == NULL) {
72
- spawner = boost::make_shared<DirectSpawner>(config);
79
+ spawner = boost::make_shared<DirectSpawner>(context);
73
80
  }
74
81
  return spawner;
75
82
  } else if (options.spawnMethod == "direct" || options.spawnMethod == "conservative") {
76
83
  boost::shared_ptr<DirectSpawner> spawner = boost::make_shared<DirectSpawner>(
77
- config);
84
+ context);
78
85
  return spawner;
79
86
  } else if (options.spawnMethod == "dummy") {
80
- syscalls::usleep(config->spawnerCreationSleepTime);
87
+ syscalls::usleep(spawnerCreationSleepTime);
81
88
  return getDummySpawner();
82
89
  } else {
83
90
  throw ArgumentException("Unknown spawn method '" + options.spawnMethod + "'");
@@ -92,16 +99,16 @@ public:
92
99
  DummySpawnerPtr getDummySpawner() {
93
100
  boost::lock_guard<boost::mutex> l(syncher);
94
101
  if (dummySpawner == NULL) {
95
- dummySpawner = boost::make_shared<DummySpawner>(config);
102
+ dummySpawner = boost::make_shared<DummySpawner>(context);
96
103
  }
97
104
  return dummySpawner;
98
105
  }
99
106
 
100
107
  /**
101
- * All created Spawner objects share the same Config object.
108
+ * All created Spawner objects share the same Context object.
102
109
  */
103
- const ConfigPtr &getConfig() const {
104
- return config;
110
+ Context *getContext() const {
111
+ return context;
105
112
  }
106
113
  };
107
114
 
@@ -28,6 +28,7 @@
28
28
 
29
29
  #include <boost/thread.hpp>
30
30
  #include <boost/bind.hpp>
31
+ #include <boost/function.hpp>
31
32
  #include <boost/foreach.hpp>
32
33
  #include <oxt/backtrace.hpp>
33
34
  #include <oxt/system_calls.hpp>
@@ -57,14 +58,16 @@ using namespace std;
57
58
  */
58
59
  class BackgroundIOCapturer {
59
60
  private:
60
- FileDescriptor fd;
61
- pid_t pid;
62
- const char *channelName;
63
- boost::mutex dataSyncher;
61
+ const FileDescriptor fd;
62
+ const pid_t pid;
63
+ const string appGroupName;
64
+ const string appLogFile;
65
+ const StaticString channelName;
66
+ mutable boost::mutex dataSyncher;
64
67
  string data;
65
68
  oxt::thread *thr;
66
- const HashedStaticString &appGroupName;
67
- const StaticString &appLogFile;
69
+ boost::function<void ()> endReachedCallback;
70
+ bool stopped;
68
71
 
69
72
  void capture() {
70
73
  TRACE_POINT();
@@ -104,16 +107,30 @@ private:
104
107
  }
105
108
  }
106
109
  }
110
+
111
+ {
112
+ boost::lock_guard<boost::mutex> l(dataSyncher);
113
+ stopped = true;
114
+ }
115
+ if (endReachedCallback != NULL) {
116
+ endReachedCallback();
117
+ }
107
118
  }
108
119
 
109
120
  public:
110
- BackgroundIOCapturer(const FileDescriptor &_fd, pid_t _pid, const char *_channelName, const HashedStaticString &_appGroupName, const StaticString &_appLogFile)
121
+ BackgroundIOCapturer(const FileDescriptor &_fd, pid_t _pid,
122
+ const string &_appGroupName,
123
+ const string &_appLogFile,
124
+ const StaticString &_channelName = P_STATIC_STRING("output"),
125
+ const StaticString &_data = StaticString())
111
126
  : fd(_fd),
112
127
  pid(_pid),
128
+ appGroupName(_appGroupName),
129
+ appLogFile(_appLogFile),
113
130
  channelName(_channelName),
131
+ data(_data.data(), _data.size()),
114
132
  thr(NULL),
115
- appGroupName(_appGroupName),
116
- appLogFile(_appLogFile)
133
+ stopped(false)
117
134
  { }
118
135
 
119
136
  ~BackgroundIOCapturer() {
@@ -137,16 +154,19 @@ public:
137
154
  "Background I/O capturer", 64 * 1024);
138
155
  }
139
156
 
140
- string stop() {
157
+ void stop() {
141
158
  TRACE_POINT();
142
- assert(thr != NULL);
143
- boost::this_thread::disable_interruption di;
144
- boost::this_thread::disable_syscall_interruption dsi;
145
- thr->interrupt_and_join();
146
- delete thr;
147
- thr = NULL;
148
- boost::lock_guard<boost::mutex> l(dataSyncher);
149
- return data;
159
+ if (thr != NULL) {
160
+ boost::this_thread::disable_interruption di;
161
+ boost::this_thread::disable_syscall_interruption dsi;
162
+ thr->interrupt_and_join();
163
+ delete thr;
164
+ thr = NULL;
165
+ }
166
+ }
167
+
168
+ void setEndReachedCallback(const boost::function<void ()> &callback) {
169
+ endReachedCallback = callback;
150
170
  }
151
171
 
152
172
  void appendToBuffer(const StaticString &dataToAdd) {
@@ -154,6 +174,16 @@ public:
154
174
  boost::lock_guard<boost::mutex> l(dataSyncher);
155
175
  data.append(dataToAdd.data(), dataToAdd.size());
156
176
  }
177
+
178
+ string getData() const {
179
+ boost::lock_guard<boost::mutex> l(dataSyncher);
180
+ return data;
181
+ }
182
+
183
+ bool isStopped() const {
184
+ boost::lock_guard<boost::mutex> l(dataSyncher);
185
+ return stopped;
186
+ }
157
187
  };
158
188
 
159
189
  typedef boost::shared_ptr<BackgroundIOCapturer> BackgroundIOCapturerPtr;
@@ -0,0 +1,1650 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2016-2017 Phusion Holding B.V.
4
+ *
5
+ * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
+ * trademarks of Phusion Holding B.V.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+ #ifndef _PASSENGER_SPAWNING_KIT_HANDSHAKE_PERFORM_H_
27
+ #define _PASSENGER_SPAWNING_KIT_HANDSHAKE_PERFORM_H_
28
+
29
+ #include <boost/thread.hpp>
30
+ #include <boost/make_shared.hpp>
31
+ #include <boost/bind.hpp>
32
+ #include <oxt/thread.hpp>
33
+ #include <oxt/system_calls.hpp>
34
+ #include <oxt/backtrace.hpp>
35
+ #include <string>
36
+ #include <vector>
37
+ #include <stdexcept>
38
+ #include <cstddef>
39
+ #include <cstdlib>
40
+ #include <cerrno>
41
+ #include <cassert>
42
+
43
+ #include <sys/types.h>
44
+ #include <dirent.h>
45
+
46
+ #include <jsoncpp/json.h>
47
+
48
+ #include <Constants.h>
49
+ #include <Exceptions.h>
50
+ #include <FileDescriptor.h>
51
+ #include <Utils.h>
52
+ #include <Utils/ScopeGuard.h>
53
+ #include <Utils/SystemTime.h>
54
+ #include <Utils/StrIntUtils.h>
55
+ #include <Core/SpawningKit/Config.h>
56
+ #include <Core/SpawningKit/Exceptions.h>
57
+ #include <Core/SpawningKit/Handshake/BackgroundIOCapturer.h>
58
+ #include <Core/SpawningKit/Handshake/Session.h>
59
+
60
+ namespace Passenger {
61
+ namespace SpawningKit {
62
+
63
+ using namespace std;
64
+ using namespace oxt;
65
+
66
+
67
+ /**
68
+ * For an introduction see README.md, section
69
+ * "The handshake and the HandshakePerform class".
70
+ */
71
+ class HandshakePerform {
72
+ private:
73
+ enum FinishState {
74
+ // The app hasn't finished spawning yet.
75
+ NOT_FINISHED,
76
+ // The app has successfully finished spawning.
77
+ FINISH_SUCCESS,
78
+ // The app has finished spawning with an error.
79
+ FINISH_ERROR,
80
+ // An internal error occurred in watchFinishSignal().
81
+ FINISH_INTERNAL_ERROR
82
+ };
83
+
84
+ HandshakeSession &session;
85
+ Config * const config;
86
+ const pid_t pid;
87
+ const FileDescriptor stdinFd;
88
+ const FileDescriptor stdoutAndErrFd;
89
+ const string alreadyReadStdoutAndErrData;
90
+
91
+
92
+ /**
93
+ * These objects captures the process's stdout and stderr while handshake is
94
+ * in progress. If handshaking fails, then any output captured by these objects
95
+ * will be stored into the resulting SpawnException's error page.
96
+ */
97
+ BackgroundIOCapturerPtr stdoutAndErrCapturer;
98
+
99
+ boost::mutex syncher;
100
+ boost::condition_variable cond;
101
+
102
+ oxt::thread *processExitWatcher;
103
+ oxt::thread *finishSignalWatcher;
104
+ bool processExited;
105
+ FinishState finishState;
106
+ string finishSignalWatcherErrorMessage;
107
+ ErrorCategory finishSignalWatcherErrorCategory;
108
+
109
+ oxt::thread *socketPingabilityWatcher;
110
+ bool socketIsNowPingable;
111
+
112
+
113
+ void initializeStdchannelsCapturing() {
114
+ if (stdoutAndErrFd != -1) {
115
+ stdoutAndErrCapturer = boost::make_shared<BackgroundIOCapturer>(
116
+ stdoutAndErrFd, pid, "output", alreadyReadStdoutAndErrData);
117
+ stdoutAndErrCapturer->setEndReachedCallback(boost::bind(
118
+ &HandshakePerform::wakeupEventLoop, this));
119
+ stdoutAndErrCapturer->start();
120
+ }
121
+ }
122
+
123
+ void startWatchingProcessExit() {
124
+ processExitWatcher = new oxt::thread(
125
+ boost::bind(&HandshakePerform::watchProcessExit, this),
126
+ "SpawningKit: process exit watcher", 64 * 1024);
127
+ }
128
+
129
+ void watchProcessExit() {
130
+ TRACE_POINT();
131
+ int ret = syscalls::waitpid(pid, NULL, 0);
132
+ if (ret >= 0 || errno == EPERM) {
133
+ boost::lock_guard<boost::mutex> l(syncher);
134
+ processExited = true;
135
+ wakeupEventLoop();
136
+ }
137
+ }
138
+
139
+ void startWatchingFinishSignal() {
140
+ finishSignalWatcher = new oxt::thread(
141
+ boost::bind(&HandshakePerform::watchFinishSignal, this),
142
+ "SpawningKit: finish signal watcher", 64 * 1024);
143
+ }
144
+
145
+ void watchFinishSignal() {
146
+ TRACE_POINT();
147
+ try {
148
+ string path = session.responseDir + "/finish";
149
+ int fd = syscalls::open(path.c_str(), O_RDONLY);
150
+ if (fd == -1) {
151
+ int e = errno;
152
+ throw FileSystemException("Error opening FIFO " + path,
153
+ e, path);
154
+ }
155
+ FdGuard guard(fd, __FILE__, __LINE__);
156
+
157
+ char buf = '0';
158
+ ssize_t ret = syscalls::read(fd, &buf, 1);
159
+ if (ret == -1) {
160
+ int e = errno;
161
+ throw FileSystemException("Error reading from FIFO " + path,
162
+ e, path);
163
+ }
164
+
165
+ guard.runNow();
166
+
167
+ boost::lock_guard<boost::mutex> l(syncher);
168
+ if (buf == '1') {
169
+ finishState = FINISH_SUCCESS;
170
+ } else {
171
+ finishState = FINISH_ERROR;
172
+ }
173
+ wakeupEventLoop();
174
+ } catch (const std::exception &e) {
175
+ boost::lock_guard<boost::mutex> l(syncher);
176
+ finishState = FINISH_INTERNAL_ERROR;
177
+ finishSignalWatcherErrorMessage = e.what();
178
+ finishSignalWatcherErrorCategory =
179
+ inferErrorCategoryFromAnotherException(e,
180
+ SPAWNING_KIT_HANDSHAKE_PERFORM);
181
+ wakeupEventLoop();
182
+ }
183
+ }
184
+
185
+ void startWatchingSocketPingability() {
186
+ socketPingabilityWatcher = new oxt::thread(
187
+ boost::bind(&HandshakePerform::watchSocketPingability, this),
188
+ "SpawningKit: socket pingability watcher", 64 * 1024);
189
+ }
190
+
191
+ void watchSocketPingability() {
192
+ TRACE_POINT();
193
+
194
+ while (true) {
195
+ unsigned long long timeout = 100000;
196
+
197
+ if (pingTcpServer("127.0.0.1", session.expectedStartPort, &timeout)) {
198
+ boost::lock_guard<boost::mutex> l(syncher);
199
+ socketIsNowPingable = true;
200
+ finishState = FINISH_SUCCESS;
201
+ wakeupEventLoop();
202
+ } else {
203
+ syscalls::usleep(50000);
204
+ }
205
+ }
206
+ }
207
+
208
+ void waitUntilSpawningFinished(boost::unique_lock<boost::mutex> &l) {
209
+ TRACE_POINT();
210
+ bool done;
211
+
212
+ do {
213
+ boost::this_thread::interruption_point();
214
+ done = checkCurrentState();
215
+ if (!done) {
216
+ MonotonicTimeUsec begin = SystemTime::getMonotonicUsec();
217
+ cond.timed_wait(l, posix_time::microseconds(session.timeoutUsec));
218
+ MonotonicTimeUsec end = SystemTime::getMonotonicUsec();
219
+ if (end - begin > session.timeoutUsec) {
220
+ session.timeoutUsec = 0;
221
+ } else {
222
+ session.timeoutUsec -= end - begin;
223
+ }
224
+ }
225
+ } while (!done);
226
+ }
227
+
228
+ bool checkCurrentState() {
229
+ TRACE_POINT();
230
+
231
+ if ((stdoutAndErrCapturer != NULL && stdoutAndErrCapturer->isStopped())
232
+ || processExited)
233
+ {
234
+ UPDATE_TRACE_POINT();
235
+ sleepShortlyToCaptureMoreStdoutStderr();
236
+ loadJourneyStateFromResponseDir();
237
+ if (session.journey.getFirstFailedStep() == UNKNOWN_JOURNEY_STEP) {
238
+ session.journey.setStepErrored(bestGuessSubprocessFailedStep(), true);
239
+ }
240
+
241
+ SpawnException e(
242
+ inferErrorCategoryFromResponseDir(INTERNAL_ERROR),
243
+ session.journey,
244
+ config);
245
+ e.setSummary("The application process exited prematurely.");
246
+ e.setSubprocessPid(pid);
247
+ e.setStdoutAndErrData(getStdoutErrData());
248
+ loadSubprocessErrorMessagesAndEnvDump(e);
249
+ throw e.finalize();
250
+ }
251
+
252
+ if (session.timeoutUsec == 0) {
253
+ UPDATE_TRACE_POINT();
254
+ sleepShortlyToCaptureMoreStdoutStderr();
255
+
256
+ loadJourneyStateFromResponseDir();
257
+ session.journey.setStepErrored(SPAWNING_KIT_HANDSHAKE_PERFORM);
258
+
259
+ SpawnException e(TIMEOUT_ERROR, session.journey, config);
260
+ e.setSubprocessPid(pid);
261
+ e.setStdoutAndErrData(getStdoutErrData());
262
+ loadSubprocessErrorMessagesAndEnvDump(e);
263
+ throw e.finalize();
264
+ }
265
+
266
+ return (config->genericApp && socketIsNowPingable)
267
+ || (!config->genericApp && finishState != NOT_FINISHED);
268
+ }
269
+
270
+ Result handleResponse() {
271
+ TRACE_POINT();
272
+ switch (finishState) {
273
+ case FINISH_SUCCESS:
274
+ return handleSuccessResponse();
275
+ case FINISH_ERROR:
276
+ handleErrorResponse();
277
+ return Result(); // Never reached, shut up compiler warning.
278
+ case FINISH_INTERNAL_ERROR:
279
+ handleInternalError();
280
+ return Result(); // Never reached, shut up compiler warning.
281
+ default:
282
+ P_BUG("Unknown finishState " + toString((int) finishState));
283
+ return Result(); // Never reached, shut up compiler warning.
284
+ }
285
+ }
286
+
287
+ Result handleSuccessResponse() {
288
+ TRACE_POINT();
289
+ Result &result = session.result;
290
+ vector<StaticString> internalFieldErrors, appSuppliedFieldErrors;
291
+
292
+ result.pid = pid;
293
+ result.stdinFd = stdinFd;
294
+ result.stdoutAndErrFd = stdoutAndErrFd;
295
+ result.spawnEndTime = SystemTime::getUsec();
296
+ result.spawnEndTimeMonotonic = SystemTime::getMonotonicUsec();
297
+
298
+ if (socketIsNowPingable) {
299
+ assert(config->genericApp || config->findFreePort);
300
+ result.sockets.push_back(Result::Socket());
301
+ Result::Socket &socket = result.sockets.back();
302
+ socket.address = "tcp://127.0.0.1:" + toString(session.expectedStartPort);
303
+ socket.protocol = "http";
304
+ socket.concurrency = -1;
305
+ socket.acceptHttpRequests = true;
306
+ }
307
+
308
+ UPDATE_TRACE_POINT();
309
+ if (fileExists(session.responseDir + "/properties.json")) {
310
+ loadResultPropertiesFromResponseDir(!socketIsNowPingable);
311
+
312
+ UPDATE_TRACE_POINT();
313
+ if (session.journey.getType() == START_PRELOADER
314
+ && !resultHasSocketWithPreloaderProtocol())
315
+ {
316
+ throwSpawnExceptionBecauseAppDidNotProvidePreloaderProtocolSockets();
317
+ } else if (session.journey.getType() != START_PRELOADER
318
+ && !resultHasSocketThatAcceptsHttpRequests())
319
+ {
320
+ throwSpawnExceptionBecauseAppDidNotProvideSocketsThatAcceptRequests();
321
+ }
322
+ }
323
+
324
+ UPDATE_TRACE_POINT();
325
+ if (result.validate(internalFieldErrors, appSuppliedFieldErrors)) {
326
+ return result;
327
+ } else {
328
+ throwSpawnExceptionBecauseOfResultValidationErrors(internalFieldErrors,
329
+ appSuppliedFieldErrors);
330
+ abort(); // never reached, shut up compiler warning
331
+ }
332
+ }
333
+
334
+ void handleErrorResponse() {
335
+ TRACE_POINT();
336
+ sleepShortlyToCaptureMoreStdoutStderr();
337
+ loadJourneyStateFromResponseDir();
338
+ if (session.journey.getFirstFailedStep() == UNKNOWN_JOURNEY_STEP) {
339
+ session.journey.setStepErrored(bestGuessSubprocessFailedStep(), true);
340
+ }
341
+
342
+ SpawnException e(
343
+ inferErrorCategoryFromResponseDir(INTERNAL_ERROR),
344
+ session.journey,
345
+ config);
346
+ e.setSummary("The web application aborted with an error during startup.");
347
+ e.setSubprocessPid(pid);
348
+ e.setStdoutAndErrData(getStdoutErrData());
349
+ loadSubprocessErrorMessagesAndEnvDump(e);
350
+ throw e.finalize();
351
+ }
352
+
353
+ void handleInternalError() {
354
+ TRACE_POINT();
355
+ sleepShortlyToCaptureMoreStdoutStderr();
356
+
357
+ loadJourneyStateFromResponseDir();
358
+ session.journey.setStepErrored(SPAWNING_KIT_HANDSHAKE_PERFORM);
359
+
360
+ SpawnException e(
361
+ finishSignalWatcherErrorCategory,
362
+ session.journey,
363
+ config);
364
+ e.setSummary("An internal error occurred while spawning an application process: "
365
+ + finishSignalWatcherErrorMessage);
366
+ e.setAdvancedProblemDetails(finishSignalWatcherErrorMessage);
367
+ e.setSubprocessPid(pid);
368
+ e.setStdoutAndErrData(getStdoutErrData());
369
+ throw e.finalize();
370
+ }
371
+
372
+ void loadResultPropertiesFromResponseDir(bool socketsRequired) {
373
+ Result &result = session.result;
374
+ string path = session.responseDir + "/properties.json";
375
+ Json::Reader reader;
376
+ Json::Value doc;
377
+ vector<string> errors;
378
+
379
+ // We already checked whether properties.json exists before invoking
380
+ // this method, so if readAll() fails then we can't be sure that
381
+ // it's an application problem. This is why we want the SystemException
382
+ // to propagate to higher layers so that there it can be turned into
383
+ // a generic filesystem-related or IO-related SpawnException, as opposed
384
+ // to one about this problem specifically.
385
+
386
+ if (!reader.parse(readAll(path), doc)) {
387
+ errors.push_back("Error parsing " + path + ": " +
388
+ reader.getFormattedErrorMessages());
389
+ throwSpawnExceptionBecauseOfResultValidationErrors(vector<string>(),
390
+ errors);
391
+ }
392
+
393
+ validateResultPropertiesFile(doc, socketsRequired, errors);
394
+ if (!errors.empty()) {
395
+ errors.insert(errors.begin(), "The following errors were detected in "
396
+ + path + ":");
397
+ throwSpawnExceptionBecauseOfResultValidationErrors(vector<string>(),
398
+ errors);
399
+ }
400
+
401
+ if (!socketsRequired && (!doc.isMember("sockets") || doc["sockets"].empty())) {
402
+ return;
403
+ }
404
+
405
+ Json::Value::iterator it, end = doc["sockets"].end();
406
+ for (it = doc["sockets"].begin(); it != end; it++) {
407
+ const Json::Value &socketDoc = *it;
408
+ result.sockets.push_back(Result::Socket());
409
+ Result::Socket &socket = result.sockets.back();
410
+
411
+ socket.address = socketDoc["address"].asString();
412
+ socket.protocol = socketDoc["protocol"].asString();
413
+ socket.concurrency = socketDoc["concurrency"].asInt();
414
+ if (socketDoc.isMember("accept_http_requests")) {
415
+ socket.acceptHttpRequests = socketDoc["accept_http_requests"].asBool();
416
+ }
417
+ if (socketDoc.isMember("description")) {
418
+ socket.description = socketDoc["description"].asString();
419
+ }
420
+ }
421
+ }
422
+
423
+ void validateResultPropertiesFile(const Json::Value &doc, bool socketsRequired,
424
+ vector<string> &errors) const
425
+ {
426
+ if (!doc.isMember("sockets")) {
427
+ if (socketsRequired) {
428
+ errors.push_back("'sockets' must be specified");
429
+ }
430
+ } else if (!doc["sockets"].isArray()) {
431
+ errors.push_back("'sockets' must be an array");
432
+ } else {
433
+ if (socketsRequired && doc["sockets"].empty()) {
434
+ errors.push_back("'sockets' must be non-empty");
435
+ return;
436
+ }
437
+
438
+ Json::Value::const_iterator it, end = doc["sockets"].end();
439
+ for (it = doc["sockets"].begin(); it != end; it++) {
440
+ const Json::Value &socketDoc = *it;
441
+
442
+ if (!socketDoc.isObject()) {
443
+ errors.push_back("'sockets[" + toString(it.index())
444
+ + "]' must be an object");
445
+ continue;
446
+ }
447
+
448
+ validateResultPropertiesFileSocketField(socketDoc,
449
+ "address", Json::stringValue, it.index(),
450
+ true, true, errors);
451
+ validateResultPropertiesFileSocketField(socketDoc,
452
+ "protocol", Json::stringValue, it.index(),
453
+ true, true, errors);
454
+ validateResultPropertiesFileSocketField(socketDoc,
455
+ "description", Json::stringValue, it.index(),
456
+ false, true, errors);
457
+ validateResultPropertiesFileSocketField(socketDoc,
458
+ "concurrency", Json::intValue, it.index(),
459
+ true, false, errors);
460
+ validateResultPropertiesFileSocketField(socketDoc,
461
+ "accept_http_requests", Json::booleanValue, it.index(),
462
+ false, false, errors);
463
+ }
464
+ }
465
+ }
466
+
467
+ void validateResultPropertiesFileSocketField(const Json::Value &doc,
468
+ const char *key, Json::ValueType type, unsigned int index, bool required,
469
+ bool requireNonEmpty, vector<string> &errors) const
470
+ {
471
+ if (!doc.isMember(key)) {
472
+ if (required) {
473
+ errors.push_back("'sockets[" + toString(index)
474
+ + "]." + key + "' must be specified");
475
+ }
476
+ } else if (doc[key].type() != type) {
477
+ const char *typeDesc;
478
+ switch (type) {
479
+ case Json::stringValue:
480
+ typeDesc = "a string";
481
+ break;
482
+ case Json::intValue:
483
+ typeDesc = "an integer";
484
+ break;
485
+ case Json::booleanValue:
486
+ typeDesc = "a boolean";
487
+ break;
488
+ default:
489
+ typeDesc = "(unknown type)";
490
+ break;
491
+ }
492
+ errors.push_back("'sockets[" + toString(index)
493
+ + "]." + key + "' must be " + typeDesc);
494
+ } else if (requireNonEmpty && doc[key].asString().empty()) {
495
+ errors.push_back("'sockets[" + toString(index)
496
+ + "]." + key + "' must be non-empty");
497
+ }
498
+ }
499
+
500
+ bool resultHasSocketWithPreloaderProtocol() const {
501
+ const vector<Result::Socket> &sockets = session.result.sockets;
502
+ vector<Result::Socket>::const_iterator it, end = sockets.end();
503
+ for (it = sockets.begin(); it != end; it++) {
504
+ if (it->protocol == "preloader") {
505
+ return true;
506
+ }
507
+ }
508
+ return false;
509
+ }
510
+
511
+ bool resultHasSocketThatAcceptsHttpRequests() const {
512
+ const vector<Result::Socket> &sockets = session.result.sockets;
513
+ vector<Result::Socket>::const_iterator it, end = sockets.end();
514
+ for (it = sockets.begin(); it != end; it++) {
515
+ if (it->acceptHttpRequests) {
516
+ return true;
517
+ }
518
+ }
519
+ return false;
520
+ }
521
+
522
+ void wakeupEventLoop() {
523
+ cond.notify_all();
524
+ }
525
+
526
+ string getStdoutErrData() const {
527
+ return getStdoutErrData(stdoutAndErrCapturer);
528
+ }
529
+
530
+ static string getStdoutErrData(const BackgroundIOCapturerPtr &stdoutAndErrCapturer) {
531
+ if (stdoutAndErrCapturer != NULL) {
532
+ return stdoutAndErrCapturer->getData();
533
+ } else {
534
+ return "(not available)";
535
+ }
536
+ }
537
+
538
+ void sleepShortlyToCaptureMoreStdoutStderr() const {
539
+ syscalls::usleep(50000);
540
+ }
541
+
542
+ void throwSpawnExceptionBecauseAppDidNotProvidePreloaderProtocolSockets() {
543
+ TRACE_POINT();
544
+ assert(!config->genericApp);
545
+
546
+ sleepShortlyToCaptureMoreStdoutStderr();
547
+
548
+ if (!config->genericApp && config->startsUsingWrapper) {
549
+ UPDATE_TRACE_POINT();
550
+ loadJourneyStateFromResponseDir();
551
+ session.journey.setStepErrored(SUBPROCESS_WRAPPER_PREPARATION, true);
552
+
553
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
554
+ e.setSubprocessPid(pid);
555
+ e.setStdoutAndErrData(getStdoutErrData());
556
+ loadAnnotationsFromEnvDumpDir(e);
557
+
558
+ if (config->wrapperSuppliedByThirdParty) {
559
+ e.setSummary("Error spawning the web application:"
560
+ " a third-party application wrapper did not"
561
+ " report any sockets to receive preloader commands on.");
562
+ } else {
563
+ e.setSummary("Error spawning the web application:"
564
+ " a " SHORT_PROGRAM_NAME "-internal application"
565
+ " wrapper did not report any sockets to receive"
566
+ " preloader commands on.");
567
+ }
568
+
569
+ if (config->wrapperSuppliedByThirdParty) {
570
+ e.setProblemDescriptionHTML(
571
+ "<p>The " PROGRAM_NAME " application server tried"
572
+ " to start the web application through a helper tool "
573
+ " called the \"wrapper\". This helper tool is not part of "
574
+ SHORT_PROGRAM_NAME ". " SHORT_PROGRAM_NAME " expected"
575
+ " the helper tool to report a socket to receive preloader"
576
+ " commands on, but the helper tool finished its startup"
577
+ " procedure without reporting such a socket.</p>");
578
+ } else {
579
+ e.setProblemDescriptionHTML(
580
+ "<p>The " PROGRAM_NAME " application server tried"
581
+ " to start the web application through a " SHORT_PROGRAM_NAME
582
+ "-internal helper tool called the \"wrapper\","
583
+ " but " SHORT_PROGRAM_NAME " encountered a bug"
584
+ " in this helper tool. " SHORT_PROGRAM_NAME " expected"
585
+ " the helper tool to report a socket to receive preloader"
586
+ " commands on, but the helper tool finished its startup"
587
+ " procedure without reporting such a socket.</p>");
588
+ }
589
+
590
+ if (config->wrapperSuppliedByThirdParty) {
591
+ e.setSolutionDescriptionHTML(
592
+ "<p class=\"sole-solution\">"
593
+ "This is a bug in the wrapper, so please contact the author of"
594
+ " the wrapper. This problem is outside " SHORT_PROGRAM_NAME
595
+ "'s control. Below follows the command that "
596
+ SHORT_PROGRAM_NAME " tried to execute, so that you can infer"
597
+ " which wrapper was used:</p>"
598
+ "<pre>" + escapeHTML(config->startCommand) + "</pre>");
599
+ } else {
600
+ e.setSolutionDescriptionHTML(
601
+ "<p class=\"sole-solution\">"
602
+ "This is a bug in " SHORT_PROGRAM_NAME "."
603
+ " <a href=\"" SUPPORT_URL "\">Please report this bug</a>"
604
+ " to the " SHORT_PROGRAM_NAME " authors.</p>");
605
+ }
606
+
607
+ throw e.finalize();
608
+
609
+ } else {
610
+ UPDATE_TRACE_POINT();
611
+ loadJourneyStateFromResponseDir();
612
+ session.journey.setStepErrored(SUBPROCESS_APP_LOAD_OR_EXEC, true);
613
+
614
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
615
+ e.setSubprocessPid(pid);
616
+ e.setStdoutAndErrData(getStdoutErrData());
617
+ loadAnnotationsFromEnvDumpDir(e);
618
+
619
+ e.setSummary("Error spawning the web application: the application"
620
+ " did not report any sockets to receive preloader commands on.");
621
+ e.setProblemDescriptionHTML(
622
+ "<p>The " PROGRAM_NAME " application server tried"
623
+ " to start the web application, but encountered a bug"
624
+ " in the application. " SHORT_PROGRAM_NAME " expected"
625
+ " the application to report a socket to receive preloader"
626
+ " commands on, but the application finished its startup"
627
+ " procedure without reporting such a socket.</p>");
628
+ e.setSolutionDescriptionHTML(
629
+ "<p class=\"sole-solution\">"
630
+ "Since this is a bug in the web application, please "
631
+ "report this problem to the application's developer. "
632
+ "This problem is outside " SHORT_PROGRAM_NAME "'s "
633
+ "control.</p>");
634
+
635
+ throw e.finalize();
636
+ }
637
+ }
638
+
639
+ void throwSpawnExceptionBecauseAppDidNotProvideSocketsThatAcceptRequests() {
640
+ TRACE_POINT();
641
+ assert(!config->genericApp);
642
+
643
+ sleepShortlyToCaptureMoreStdoutStderr();
644
+
645
+ if (!config->genericApp && config->startsUsingWrapper) {
646
+ UPDATE_TRACE_POINT();
647
+ loadJourneyStateFromResponseDir();
648
+ switch (session.journey.getType()) {
649
+ case SPAWN_DIRECTLY:
650
+ case START_PRELOADER:
651
+ session.journey.setStepErrored(SUBPROCESS_WRAPPER_PREPARATION, true);
652
+ break;
653
+ case SPAWN_THROUGH_PRELOADER:
654
+ session.journey.setStepErrored(SUBPROCESS_PREPARE_AFTER_FORKING_FROM_PRELOADER, true);
655
+ break;
656
+ default:
657
+ P_BUG("Unknown journey type " << (int) session.journey.getType());
658
+ }
659
+
660
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
661
+ e.setSubprocessPid(pid);
662
+ e.setStdoutAndErrData(getStdoutErrData());
663
+ loadAnnotationsFromEnvDumpDir(e);
664
+
665
+ if (config->wrapperSuppliedByThirdParty) {
666
+ e.setSummary("Error spawning the web application:"
667
+ " a third-party application wrapper did not"
668
+ " report any sockets to receive requests on.");
669
+ } else {
670
+ e.setSummary("Error spawning the web application:"
671
+ " a " SHORT_PROGRAM_NAME "-internal application"
672
+ " wrapper did not report any sockets to receive"
673
+ " requests on.");
674
+ }
675
+
676
+ if (config->wrapperSuppliedByThirdParty) {
677
+ e.setProblemDescriptionHTML(
678
+ "<p>The " PROGRAM_NAME " application server tried"
679
+ " to start the web application through a helper tool"
680
+ " called the \"wrapper\". This helper tool is not part of "
681
+ SHORT_PROGRAM_NAME ". " SHORT_PROGRAM_NAME " expected"
682
+ " the helper tool to report a socket to receive requests"
683
+ " on, but the helper tool finished its startup procedure"
684
+ " without reporting such a socket.</p>");
685
+ } else {
686
+ e.setProblemDescriptionHTML(
687
+ "<p>The " PROGRAM_NAME " application server tried"
688
+ " to start the web application through a " SHORT_PROGRAM_NAME
689
+ "-internal helper tool called the \"wrapper\","
690
+ " but " SHORT_PROGRAM_NAME " encountered a bug"
691
+ " in this helper tool. " SHORT_PROGRAM_NAME " expected"
692
+ " the helper tool to report a socket to receive requests"
693
+ " on, but the helper tool finished its startup procedure"
694
+ " without reporting such a socket.</p>");
695
+ }
696
+
697
+ if (config->wrapperSuppliedByThirdParty) {
698
+ e.setSolutionDescriptionHTML(
699
+ "<p class=\"sole-solution\">"
700
+ "This is a bug in the wrapper, so please contact the author of"
701
+ " the wrapper. This problem is outside " SHORT_PROGRAM_NAME
702
+ "'s control. Below follows the command that "
703
+ SHORT_PROGRAM_NAME " tried to execute, so that you can infer"
704
+ " which wrapper was used:</p>"
705
+ "<pre>" + escapeHTML(config->startCommand) + "</pre>");
706
+ } else {
707
+ e.setSolutionDescriptionHTML(
708
+ "<p class=\"sole-solution\">"
709
+ "This is a bug in " SHORT_PROGRAM_NAME "."
710
+ " <a href=\"" SUPPORT_URL "\">Please report this bug</a>"
711
+ " to the " SHORT_PROGRAM_NAME " authors.</p>");
712
+ }
713
+
714
+ throw e.finalize();
715
+
716
+ } else {
717
+ UPDATE_TRACE_POINT();
718
+ loadJourneyStateFromResponseDir();
719
+ switch (session.journey.getType()) {
720
+ case SPAWN_DIRECTLY:
721
+ case START_PRELOADER:
722
+ session.journey.setStepErrored(SUBPROCESS_APP_LOAD_OR_EXEC, true);
723
+ break;
724
+ case SPAWN_THROUGH_PRELOADER:
725
+ session.journey.setStepErrored(SUBPROCESS_PREPARE_AFTER_FORKING_FROM_PRELOADER, true);
726
+ break;
727
+ default:
728
+ P_BUG("Unknown journey type " << (int) session.journey.getType());
729
+ }
730
+
731
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
732
+ e.setSubprocessPid(pid);
733
+ e.setStdoutAndErrData(getStdoutErrData());
734
+ loadAnnotationsFromEnvDumpDir(e);
735
+
736
+ e.setSummary("Error spawning the web application: the application"
737
+ " did not report any sockets to receive requests on.");
738
+ e.setProblemDescriptionHTML(
739
+ "<p>The " PROGRAM_NAME " application server tried"
740
+ " to start the web application, but encountered a bug"
741
+ " in the application. " SHORT_PROGRAM_NAME " expected"
742
+ " the application to report a socket to receive requests"
743
+ " on, but the application finished its startup procedure"
744
+ " without reporting such a socket.</p>");
745
+ e.setSolutionDescriptionHTML(
746
+ "<p class=\"sole-solution\">"
747
+ "Since this is a bug in the web application, please "
748
+ "report this problem to the application's developer. "
749
+ "This problem is outside " SHORT_PROGRAM_NAME "'s "
750
+ "control.</p>");
751
+
752
+ throw e.finalize();
753
+ }
754
+ }
755
+
756
+ template<typename StringType>
757
+ void throwSpawnExceptionBecauseOfResultValidationErrors(
758
+ const vector<StringType> &internalFieldErrors,
759
+ const vector<StringType> &appSuppliedFieldErrors)
760
+ {
761
+ TRACE_POINT();
762
+ string message;
763
+ typename vector<StringType>::const_iterator it, end;
764
+
765
+ sleepShortlyToCaptureMoreStdoutStderr();
766
+
767
+ if (!internalFieldErrors.empty()) {
768
+ UPDATE_TRACE_POINT();
769
+ loadJourneyStateFromResponseDir();
770
+ session.journey.setStepErrored(SPAWNING_KIT_HANDSHAKE_PERFORM, true);
771
+
772
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
773
+ e.setSubprocessPid(pid);
774
+ e.setStdoutAndErrData(getStdoutErrData());
775
+ e.setAdvancedProblemDetails(toString(internalFieldErrors));
776
+
777
+ e.setSummary("Error spawning the web application:"
778
+ " a bug in " SHORT_PROGRAM_NAME " caused the"
779
+ " spawn result to be invalid: "
780
+ + toString(internalFieldErrors));
781
+
782
+ message = "<p>The " PROGRAM_NAME " application server tried"
783
+ " to start the web application, but encountered a bug"
784
+ " in " SHORT_PROGRAM_NAME " itself. The errors are as"
785
+ " follows:</p>"
786
+ "<ul>";
787
+ end = internalFieldErrors.end();
788
+ for (it = internalFieldErrors.begin(); it != end; it++) {
789
+ message.append("<li>" + escapeHTML(*it) + "</li>");
790
+ }
791
+ message.append("</ul>");
792
+ e.setProblemDescriptionHTML(message);
793
+
794
+ e.setSolutionDescriptionHTML(
795
+ "<p class=\"sole-solution\">"
796
+ "This is a bug in " SHORT_PROGRAM_NAME "."
797
+ " <a href=\"" SUPPORT_URL "\">Please report this bug</a>"
798
+ " to the " SHORT_PROGRAM_NAME " authors.</p>");
799
+
800
+ throw e.finalize();
801
+
802
+ } else if (!config->genericApp && config->startsUsingWrapper) {
803
+ UPDATE_TRACE_POINT();
804
+ loadJourneyStateFromResponseDir();
805
+ session.journey.setStepErrored(SUBPROCESS_WRAPPER_PREPARATION, true);
806
+
807
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
808
+ e.setSubprocessPid(pid);
809
+ e.setStdoutAndErrData(getStdoutErrData());
810
+ e.setAdvancedProblemDetails(toString(appSuppliedFieldErrors));
811
+ loadAnnotationsFromEnvDumpDir(e);
812
+
813
+ if (config->wrapperSuppliedByThirdParty) {
814
+ e.setSummary("Error spawning the web application:"
815
+ " a bug in a third-party application wrapper caused"
816
+ " the spawn result to be invalid: "
817
+ + toString(appSuppliedFieldErrors));
818
+ } else {
819
+ e.setSummary("Error spawning the web application:"
820
+ " a bug in a " SHORT_PROGRAM_NAME "-internal"
821
+ " application wrapper caused the"
822
+ " spawn result to be invalid: "
823
+ + toString(appSuppliedFieldErrors));
824
+ }
825
+
826
+ if (config->wrapperSuppliedByThirdParty) {
827
+ message = "<p>The " PROGRAM_NAME " application server tried"
828
+ " to start the web application through a helper tool"
829
+ " called the \"wrapper\". This helper tool is not part of "
830
+ SHORT_PROGRAM_NAME ". " SHORT_PROGRAM_NAME " expected"
831
+ " the helper tool to communicate back various information"
832
+ " about the application's startup procedure, but the tool"
833
+ " did not communicate back correctly."
834
+ " The errors are as follows:</p>"
835
+ "<ul>";
836
+ } else {
837
+ message = "<p>The " PROGRAM_NAME " application server tried"
838
+ " to start the web application through a " SHORT_PROGRAM_NAME
839
+ "-internal helper tool (called the \"wrapper\"),"
840
+ " but " SHORT_PROGRAM_NAME " encountered a bug"
841
+ " in this helper tool. " SHORT_PROGRAM_NAME " expected"
842
+ " the helper tool to communicate back various information"
843
+ " about the application's startup procedure, but the tool"
844
+ " did not communicate back correctly."
845
+ " The errors are as follows:</p>"
846
+ "<ul>";
847
+ }
848
+ end = appSuppliedFieldErrors.end();
849
+ for (it = appSuppliedFieldErrors.begin(); it != end; it++) {
850
+ message.append("<li>" + escapeHTML(*it) + "</li>");
851
+ }
852
+ message.append("</ul>");
853
+ e.setProblemDescriptionHTML(message);
854
+
855
+ if (config->wrapperSuppliedByThirdParty) {
856
+ e.setSolutionDescriptionHTML(
857
+ "<p class=\"sole-solution\">"
858
+ "This is a bug in the wrapper, so please contact the author of"
859
+ " the wrapper. This problem is outside " SHORT_PROGRAM_NAME
860
+ "'s control. Below follows the command that "
861
+ SHORT_PROGRAM_NAME " tried to execute, so that you can infer"
862
+ " which wrapper was used:</p>"
863
+ "<pre>" + escapeHTML(config->startCommand) + "</pre>");
864
+ } else {
865
+ e.setSolutionDescriptionHTML(
866
+ "<p class=\"sole-solution\">"
867
+ "This is a bug in " SHORT_PROGRAM_NAME "."
868
+ " <a href=\"" SUPPORT_URL "\">Please report this bug</a>"
869
+ " to the " SHORT_PROGRAM_NAME " authors.</p>");
870
+ }
871
+
872
+ throw e.finalize();
873
+
874
+ } else {
875
+ UPDATE_TRACE_POINT();
876
+ loadJourneyStateFromResponseDir();
877
+ session.journey.setStepErrored(SUBPROCESS_APP_LOAD_OR_EXEC, true);
878
+
879
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
880
+ e.setSummary("Error spawning the web application:"
881
+ " the application's spawn response is invalid: "
882
+ + toString(appSuppliedFieldErrors));
883
+ e.setAdvancedProblemDetails(toString(appSuppliedFieldErrors));
884
+ e.setSubprocessPid(pid);
885
+ e.setStdoutAndErrData(getStdoutErrData());
886
+ loadAnnotationsFromEnvDumpDir(e);
887
+
888
+ message = "<p>The " PROGRAM_NAME " application server tried"
889
+ " to start the web application, but encountered a bug"
890
+ " in the application. " SHORT_PROGRAM_NAME " expected"
891
+ " the application to communicate back various information"
892
+ " about its startup procedure, but the application"
893
+ " did not communicate back that correctly."
894
+ " The errors are as follows:</p>"
895
+ "<ul>";
896
+ end = appSuppliedFieldErrors.end();
897
+ for (it = appSuppliedFieldErrors.begin(); it != end; it++) {
898
+ message.append("<li>" + escapeHTML(*it) + "</li>");
899
+ }
900
+ message.append("</ul>");
901
+ e.setProblemDescriptionHTML(message);
902
+
903
+ if (config->genericApp) {
904
+ e.setSolutionDescriptionHTML(
905
+ "<p class=\"sole-solution\">"
906
+ "Since this is a bug in the web application, please "
907
+ "report this problem to the application's developer. "
908
+ "This problem is outside " SHORT_PROGRAM_NAME "'s "
909
+ "control.</p>");
910
+ } else {
911
+ e.setSolutionDescriptionHTML(
912
+ "<p class=\"sole-solution\">"
913
+ "This is a bug in " SHORT_PROGRAM_NAME "."
914
+ " <a href=\"" SUPPORT_URL "\">Please report this bug</a>"
915
+ " to the " SHORT_PROGRAM_NAME " authors.</p>");
916
+ }
917
+
918
+ throw e.finalize();
919
+ }
920
+ }
921
+
922
+ ErrorCategory inferErrorCategoryFromResponseDir(ErrorCategory defaultValue) const {
923
+ TRACE_POINT();
924
+ if (fileExists(session.responseDir + "/error/category")) {
925
+ string value = strip(readAll(session.responseDir + "/error/category"));
926
+ ErrorCategory category = stringToErrorCategory(value);
927
+
928
+ if (category == UNKNOWN_ERROR_CATEGORY) {
929
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
930
+ e.setStdoutAndErrData(getStdoutErrData());
931
+ e.setSubprocessPid(pid);
932
+ loadAnnotationsFromEnvDumpDir(e);
933
+
934
+ if (!config->genericApp && config->startsUsingWrapper) {
935
+ if (config->wrapperSuppliedByThirdParty) {
936
+ e.setSummary(
937
+ "An error occurred while spawning an application process: "
938
+ "the application wrapper (which is not part of "
939
+ SHORT_PROGRAM_NAME
940
+ ") reported an invalid error category: "
941
+ + value);
942
+ } else {
943
+ e.setSummary(
944
+ "An error occurred while spawning an application process: "
945
+ "the application wrapper (which is internal to "
946
+ SHORT_PROGRAM_NAME
947
+ ") reported an invalid error category: "
948
+ + value);
949
+ }
950
+ } else {
951
+ e.setSummary(
952
+ "An error occurred while spawning an application process: "
953
+ "the application reported an invalid error category: "
954
+ + value);
955
+ }
956
+
957
+ if (!config->genericApp && config->startsUsingWrapper) {
958
+ if (config->wrapperSuppliedByThirdParty) {
959
+ e.setProblemDescriptionHTML(
960
+ "<p>The " PROGRAM_NAME " application server tried"
961
+ " to start the web application through a"
962
+ " helper tool called the \"wrapper\". This helper tool "
963
+ " is not part of " SHORT_PROGRAM_NAME ". The tool "
964
+ " encountered an error, so " SHORT_PROGRAM_NAME
965
+ " expected the tool to report details about that error."
966
+ " But the tool communicated back in an invalid format:</p>"
967
+ "<ul>"
968
+ "<li>In file: " + escapeHTML(session.responseDir) + "/error/category</li>"
969
+ "<li>Content: <code>" + escapeHTML(value) + "</code></li>"
970
+ "</ul>");
971
+ e.setSolutionDescriptionHTML(
972
+ "<p class=\"sole-solution\">"
973
+ "This is a bug in the wrapper, so please contact the author of"
974
+ " the wrapper. This problem is outside " SHORT_PROGRAM_NAME
975
+ "'s control. Below follows the command that "
976
+ SHORT_PROGRAM_NAME " tried to execute, so that you can infer"
977
+ " which wrapper was used:</p>"
978
+ "<pre>" + escapeHTML(config->startCommand) + "</pre>");
979
+ } else {
980
+ e.setProblemDescriptionHTML(
981
+ "<p>The " PROGRAM_NAME " application server tried"
982
+ " to start the web application through a " SHORT_PROGRAM_NAME
983
+ "-internal helper tool called the \"wrapper\"."
984
+ " The tool encountered an error, so "
985
+ SHORT_PROGRAM_NAME " expected the tool to report"
986
+ " details about that error. But the tool communicated back"
987
+ " in an invalid format:</p>"
988
+ "<ul>"
989
+ "<li>In file: " + escapeHTML(session.responseDir) + "/error/category</li>"
990
+ "<li>Content: <code>" + escapeHTML(value) + "</code></li>"
991
+ "</ul>");
992
+ e.setSolutionDescriptionHTML(
993
+ "<p class=\"sole-solution\">"
994
+ "This is a bug in " SHORT_PROGRAM_NAME "."
995
+ " <a href=\"" SUPPORT_URL "\">Please report this bug</a>"
996
+ " to the " SHORT_PROGRAM_NAME " authors.</p>");
997
+ }
998
+ } else {
999
+ e.setProblemDescriptionHTML(
1000
+ "<p>The " PROGRAM_NAME " application server tried"
1001
+ " to start the web application. The application encountered "
1002
+ " an error and tried to report details about the error back to "
1003
+ SHORT_PROGRAM_NAME ". But the application communicated back"
1004
+ " in an invalid format:</p>"
1005
+ "<ul>"
1006
+ "<li>In file: " + escapeHTML(session.responseDir) + "/error/category</li>"
1007
+ "<li>Content: <code>" + escapeHTML(value) + "</code></li>"
1008
+ "</ul>");
1009
+ e.setSolutionDescriptionHTML(
1010
+ "<p class=\"sole-solution\">"
1011
+ "This is a bug in the web application, please "
1012
+ "report this problem to the application's developer. "
1013
+ "This problem is outside " SHORT_PROGRAM_NAME "'s "
1014
+ "control.</p>");
1015
+ }
1016
+
1017
+ throw e.finalize();
1018
+ } else {
1019
+ return category;
1020
+ }
1021
+ } else {
1022
+ return defaultValue;
1023
+ }
1024
+ }
1025
+
1026
+ void loadJourneyStateFromResponseDir() {
1027
+ loadJourneyStateFromResponseDir(session, pid, stdoutAndErrCapturer);
1028
+ }
1029
+
1030
+ static void loadJourneyStateFromResponseDir(HandshakeSession &session, pid_t pid,
1031
+ const BackgroundIOCapturerPtr &stdoutAndErrCapturer,
1032
+ JourneyStep firstStep, JourneyStep lastStep)
1033
+ {
1034
+ TRACE_POINT();
1035
+ JourneyStep step;
1036
+
1037
+ for (step = firstStep; step < lastStep; step = JourneyStep((int) step + 1)) {
1038
+ if (!session.journey.hasStep(step)) {
1039
+ continue;
1040
+ }
1041
+
1042
+ string stepString = journeyStepToStringLowerCase(step);
1043
+ string stepDir = session.responseDir + "/steps/" + stepString;
1044
+ if (!fileExists(stepDir + "/state")) {
1045
+ P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
1046
+ << ": state file does not exist");
1047
+ continue;
1048
+ }
1049
+
1050
+ loadJourneyStateFromResponseDirForSpecificStep(
1051
+ session, pid, stdoutAndErrCapturer, step, stepDir);
1052
+ }
1053
+ }
1054
+
1055
+ static void loadJourneyStateFromResponseDirForSpecificStep(HandshakeSession &session,
1056
+ pid_t pid, const BackgroundIOCapturerPtr &stdoutAndErrCapturer,
1057
+ JourneyStep step, const string &stepDir)
1058
+ {
1059
+ TRACE_POINT_WITH_DATA(journeyStepToString(step).data());
1060
+ string summary;
1061
+ string value = strip(readAll(stepDir + "/state"));
1062
+ JourneyStepState state = stringToJourneyStepState(value);
1063
+ const Config *config = session.config;
1064
+
1065
+ if (value.empty()) {
1066
+ P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
1067
+ << ": state file is empty");
1068
+ return;
1069
+ }
1070
+
1071
+ P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
1072
+ << ": setting state to " << value);
1073
+
1074
+ try {
1075
+ UPDATE_TRACE_POINT();
1076
+ switch (state) {
1077
+ case STEP_NOT_STARTED:
1078
+ // SpawnEnvSetupper explicitly sets the SUBPROCESS_OS_SHELL
1079
+ // step state to STEP_NOT_STARTED if it determines that it
1080
+ // should not execute the next command through the shell.
1081
+ session.journey.setStepNotStarted(step, true);
1082
+ break;
1083
+ case STEP_IN_PROGRESS:
1084
+ session.journey.setStepInProgress(step, true);
1085
+ break;
1086
+ case STEP_PERFORMED:
1087
+ session.journey.setStepPerformed(step, true);
1088
+ break;
1089
+ case STEP_ERRORED:
1090
+ session.journey.setStepErrored(step, true);
1091
+ break;
1092
+ default:
1093
+ session.journey.setStepErrored(step, true);
1094
+
1095
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
1096
+ e.setStdoutAndErrData(getStdoutErrData(stdoutAndErrCapturer));
1097
+ e.setSubprocessPid(pid);
1098
+ loadAnnotationsFromEnvDumpDir(e, session);
1099
+
1100
+ if (!config->genericApp && config->startsUsingWrapper) {
1101
+ if (config->wrapperSuppliedByThirdParty) {
1102
+ e.setSummary(
1103
+ "An error occurred while spawning an application process: "
1104
+ "the application wrapper (which is not part of " SHORT_PROGRAM_NAME
1105
+ ") reported an invalid progress step state for step "
1106
+ + journeyStepToString(step) + ": " + value);
1107
+ } else {
1108
+ e.setSummary(
1109
+ "An error occurred while spawning an application process: "
1110
+ "the application wrapper (which is internal to " SHORT_PROGRAM_NAME
1111
+ ") reported an invalid progress step state for step "
1112
+ + journeyStepToString(step) + ": " + value);
1113
+ }
1114
+ } else {
1115
+ e.setSummary(
1116
+ "An error occurred while spawning an application process: "
1117
+ "the application reported an invalid progress step state for step "
1118
+ + journeyStepToString(step) + ": " + value);
1119
+ }
1120
+
1121
+ if (!config->genericApp && config->startsUsingWrapper) {
1122
+ if (config->wrapperSuppliedByThirdParty) {
1123
+ e.setProblemDescriptionHTML(
1124
+ "<p>The " PROGRAM_NAME " application server tried"
1125
+ " to start the web application through a"
1126
+ " helper tool called the \"wrapper\". This helper tool"
1127
+ " is not part of " SHORT_PROGRAM_NAME ". "
1128
+ SHORT_PROGRAM_NAME " expected the helper tool to"
1129
+ " report about its startup progress, but the tool"
1130
+ " communicated back an invalid answer:</p>"
1131
+ "<ul>"
1132
+ "<li>In file: " + escapeHTML(stepDir) + "/state</li>"
1133
+ "<li>Content: <code>" + escapeHTML(value) + "</code></li>"
1134
+ "</ul>");
1135
+ e.setSolutionDescriptionHTML(
1136
+ "<p class=\"sole-solution\">"
1137
+ "This is a bug in the wrapper, so please contact the author of"
1138
+ " the wrapper. This problem is outside " SHORT_PROGRAM_NAME
1139
+ "'s control. Below follows the command that "
1140
+ SHORT_PROGRAM_NAME " tried to execute, so that you can infer"
1141
+ " which wrapper was used:</p>"
1142
+ "<pre>" + escapeHTML(config->startCommand) + "</pre>");
1143
+ } else {
1144
+ e.setProblemDescriptionHTML(
1145
+ "<p>The " PROGRAM_NAME " application server tried"
1146
+ " to start the web application through a " SHORT_PROGRAM_NAME
1147
+ "-internal helper tool called the \"wrapper\","
1148
+ " but " SHORT_PROGRAM_NAME " encountered a bug"
1149
+ " in this helper tool. " SHORT_PROGRAM_NAME " expected"
1150
+ " the helper tool to report about its startup progress,"
1151
+ " but the tool communicated back an invalid answer:</p>"
1152
+ "<ul>"
1153
+ "<li>In file: " + escapeHTML(stepDir) + "/state</li>"
1154
+ "<li>Content: <code>" + escapeHTML(value) + "</code></li>"
1155
+ "</ul>");
1156
+ e.setSolutionDescriptionHTML(
1157
+ "<p class=\"sole-solution\">"
1158
+ "This is a bug in " SHORT_PROGRAM_NAME "."
1159
+ " <a href=\"" SUPPORT_URL "\">Please report this bug</a>"
1160
+ " to the " SHORT_PROGRAM_NAME " authors.</p>");
1161
+ }
1162
+ } else {
1163
+ e.setProblemDescriptionHTML(
1164
+ "<p>The " PROGRAM_NAME " application server tried"
1165
+ " to start the web application, and expected the application"
1166
+ " to report about its startup progress. But the application"
1167
+ " communicated back an invalid answer:</p>"
1168
+ "<ul>"
1169
+ "<li>In file: " + escapeHTML(stepDir) + "/state</li>"
1170
+ "<li>Content: <code>" + escapeHTML(value) + "</code></li>"
1171
+ "</ul>");
1172
+ e.setSolutionDescriptionHTML(
1173
+ "<p class=\"sole-solution\">"
1174
+ "This is a bug in the web application, please "
1175
+ "report this problem to the application's developer. "
1176
+ "This problem is outside " SHORT_PROGRAM_NAME "'s "
1177
+ "control.</p>");
1178
+ }
1179
+
1180
+ throw e.finalize();
1181
+ break;
1182
+ };
1183
+ } catch (const RuntimeException &originalException) {
1184
+ UPDATE_TRACE_POINT();
1185
+ session.journey.setStepErrored(step, true);
1186
+
1187
+ SpawnException e(INTERNAL_ERROR, session.journey, config);
1188
+ e.setStdoutAndErrData(getStdoutErrData(stdoutAndErrCapturer));
1189
+ e.setSubprocessPid(pid);
1190
+ loadAnnotationsFromEnvDumpDir(e, session);
1191
+
1192
+ if (!config->genericApp && config->startsUsingWrapper) {
1193
+ if (config->wrapperSuppliedByThirdParty) {
1194
+ e.setSummary("An error occurred while spawning an application process: "
1195
+ "the application wrapper (which is not part of " SHORT_PROGRAM_NAME
1196
+ ") reported an invalid progress step state for step "
1197
+ + journeyStepToString(step) + ": "
1198
+ + StaticString(originalException.what()));
1199
+ } else {
1200
+ e.setSummary("An error occurred while spawning an application process: "
1201
+ "the application wrapper (which is internal to " SHORT_PROGRAM_NAME
1202
+ ") reported an invalid progress step state for step "
1203
+ + journeyStepToString(step) + ": "
1204
+ + StaticString(originalException.what()));
1205
+ }
1206
+ } else {
1207
+ e.setSummary("An error occurred while spawning an application process: "
1208
+ "the application reported an invalid progress step state for step "
1209
+ + journeyStepToString(step) + ": "
1210
+ + StaticString(originalException.what()));
1211
+ }
1212
+
1213
+ if (!config->genericApp && config->startsUsingWrapper) {
1214
+ if (config->wrapperSuppliedByThirdParty) {
1215
+ e.setProblemDescriptionHTML(
1216
+ "<p>The " PROGRAM_NAME " application server tried"
1217
+ " to start the web application through a "
1218
+ " helper tool called the \"wrapper\". This helper tool"
1219
+ " is not part of " SHORT_PROGRAM_NAME ". "
1220
+ SHORT_PROGRAM_NAME " expected the helper tool to"
1221
+ " report about its startup progress, but the tool"
1222
+ " communicated back an invalid answer:</p>"
1223
+ "<ul>"
1224
+ "<li>In file: " + escapeHTML(stepDir) + "/state</li>"
1225
+ "<li>Error: " + escapeHTML(originalException.what()) + "</li>"
1226
+ "</ul>");
1227
+ e.setSolutionDescriptionHTML(
1228
+ "<p class=\"sole-solution\">"
1229
+ "This is a bug in the wrapper, so please contact the author of"
1230
+ " the wrapper. This problem is outside " SHORT_PROGRAM_NAME
1231
+ "'s control. Below follows the command that "
1232
+ SHORT_PROGRAM_NAME " tried to execute, so that you can infer"
1233
+ " which wrapper was used:</p>"
1234
+ "<pre>" + escapeHTML(config->startCommand) + "</pre>");
1235
+ } else {
1236
+ e.setProblemDescriptionHTML(
1237
+ "<p>The " PROGRAM_NAME " application server tried"
1238
+ " to start the web application through a " SHORT_PROGRAM_NAME
1239
+ "-internal helper tool called the \"wrapper\","
1240
+ " but " SHORT_PROGRAM_NAME " encountered a bug"
1241
+ " in this helper tool. " SHORT_PROGRAM_NAME " expected"
1242
+ " the helper tool to report about its startup progress,"
1243
+ " but the tool communicated back an invalid answer:</p>"
1244
+ "<ul>"
1245
+ "<li>In file: " + escapeHTML(stepDir) + "/state</li>"
1246
+ "<li>Error: " + escapeHTML(originalException.what()) + "</li>"
1247
+ "</ul>");
1248
+ e.setSolutionDescriptionHTML(
1249
+ "<p class=\"sole-solution\">"
1250
+ "This is a bug in " SHORT_PROGRAM_NAME "."
1251
+ " <a href=\"" SUPPORT_URL "\">Please report this bug</a>"
1252
+ " to the " SHORT_PROGRAM_NAME " authors.</p>");
1253
+ }
1254
+ } else {
1255
+ e.setProblemDescriptionHTML(
1256
+ "<p>The " PROGRAM_NAME " application server tried"
1257
+ " to start the web application, and expected the application"
1258
+ " to report about its startup progress. But the application"
1259
+ " communicated back an invalid answer:</p>"
1260
+ "<ul>"
1261
+ "<li>In file: " + escapeHTML(stepDir) + "/state</li>"
1262
+ "<li>Error: " + escapeHTML(originalException.what()) + "</li>"
1263
+ "</ul>");
1264
+ e.setSolutionDescriptionHTML(
1265
+ "<p class=\"sole-solution\">"
1266
+ "This is a bug in the web application, please "
1267
+ "report this problem to the application's developer. "
1268
+ "This problem is outside " SHORT_PROGRAM_NAME "'s "
1269
+ "control.</p>");
1270
+ }
1271
+
1272
+ throw e.finalize();
1273
+ }
1274
+
1275
+ UPDATE_TRACE_POINT();
1276
+ if (fileExists(stepDir + "/begin_time_monotonic")) {
1277
+ value = readAll(stepDir + "/begin_time_monotonic");
1278
+ MonotonicTimeUsec beginTimeMonotonic = atof(value.c_str()) * 1000000;
1279
+ P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
1280
+ << ": monotonic begin time is \"" << cEscapeString(value) << "\"");
1281
+ session.journey.setStepBeginTime(step, beginTimeMonotonic);
1282
+ } else if (fileExists(stepDir + "/begin_time")) {
1283
+ value = readAll(stepDir + "/begin_time");
1284
+ unsigned long long beginTime = atof(value.c_str()) * 1000000;
1285
+ MonotonicTimeUsec beginTimeMonotonic = usecTimestampToMonoTime(beginTime);
1286
+ P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
1287
+ << ": begin time is \"" << cEscapeString(value) << "\", monotonic conversion is "
1288
+ << doubleToString(beginTimeMonotonic / 1000000.0));
1289
+ session.journey.setStepBeginTime(step, beginTimeMonotonic);
1290
+ } else {
1291
+ P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
1292
+ << ": no begin time known");
1293
+ }
1294
+
1295
+ UPDATE_TRACE_POINT();
1296
+ if (fileExists(stepDir + "/end_time_monotonic")) {
1297
+ value = readAll(stepDir + "/end_time_monotonic");
1298
+ MonotonicTimeUsec endTimeMonotonic = atof(value.c_str()) * 1000000;
1299
+ P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
1300
+ << ": monotonic end time is \"" << cEscapeString(value) << "\"");
1301
+ session.journey.setStepEndTime(step, endTimeMonotonic);
1302
+ } else if (fileExists(stepDir + "/end_time")) {
1303
+ value = readAll(stepDir + "/end_time");
1304
+ unsigned long long endTime = atof(value.c_str()) * 1000000;
1305
+ MonotonicTimeUsec endTimeMonotonic = usecTimestampToMonoTime(endTime);
1306
+ P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
1307
+ << ": end time is \"" << cEscapeString(value) << "\", monotonic conversion is "
1308
+ << doubleToString(endTimeMonotonic / 1000000.0));
1309
+ session.journey.setStepEndTime(step, endTimeMonotonic);
1310
+ } else {
1311
+ P_DEBUG("[App " << pid << " journey] Step " << journeyStepToString(step)
1312
+ << ": no end time known");
1313
+ }
1314
+ }
1315
+
1316
+ static MonotonicTimeUsec usecTimestampToMonoTime(unsigned long long timestamp) {
1317
+ unsigned long long now = SystemTime::getUsec();
1318
+ MonotonicTimeUsec nowMono = SystemTime::getMonotonicUsec();
1319
+ unsigned long long diff;
1320
+
1321
+ if (now > nowMono) {
1322
+ diff = now - nowMono;
1323
+ return timestamp - diff;
1324
+ } else {
1325
+ diff = nowMono - now;
1326
+ return timestamp + diff;
1327
+ }
1328
+ }
1329
+
1330
+ void loadSubprocessErrorMessagesAndEnvDump(SpawnException &e) const {
1331
+ TRACE_POINT();
1332
+ const string &responseDir = session.responseDir;
1333
+ const string &envDumpDir = session.envDumpDir;
1334
+
1335
+ if (fileExists(responseDir + "/error/summary")) {
1336
+ e.setSummary(strip(readAll(responseDir + "/error/summary")));
1337
+ }
1338
+
1339
+ if (e.getAdvancedProblemDetails().empty()
1340
+ && fileExists(responseDir + "/error/advanced_problem_details"))
1341
+ {
1342
+ e.setAdvancedProblemDetails(strip(readAll(responseDir
1343
+ + "/error/advanced_problem_details")));
1344
+ }
1345
+
1346
+ if (fileExists(responseDir + "/error/problem_description.html")) {
1347
+ e.setProblemDescriptionHTML(readAll(responseDir + "/error/problem_description.html"));
1348
+ } else if (fileExists(responseDir + "/error/problem_description.txt")) {
1349
+ e.setProblemDescriptionHTML(escapeHTML(strip(readAll(
1350
+ responseDir + "/error/problem_description.txt"))));
1351
+ }
1352
+
1353
+ if (fileExists(responseDir + "/error/solution_description.html")) {
1354
+ e.setSolutionDescriptionHTML(readAll(responseDir + "/error/solution_description.html"));
1355
+ } else if (fileExists(responseDir + "/error/solution_description.txt")) {
1356
+ e.setSolutionDescriptionHTML(escapeHTML(strip(readAll(
1357
+ responseDir + "/error/solution_description.txt"))));
1358
+ }
1359
+
1360
+ string envvars, userInfo, ulimits;
1361
+ loadBasicInfoFromEnvDumpDir(envDumpDir, envvars, userInfo, ulimits);
1362
+ e.setSubprocessEnvvars(envvars);
1363
+ e.setSubprocessUserInfo(userInfo);
1364
+ e.setSubprocessUlimits(ulimits);
1365
+
1366
+ loadAnnotationsFromEnvDumpDir(e);
1367
+ }
1368
+
1369
+ static void doClosedir(DIR *dir) {
1370
+ closedir(dir);
1371
+ }
1372
+
1373
+ void loadAnnotationsFromEnvDumpDir(SpawnException &e) const {
1374
+ loadAnnotationsFromEnvDumpDir(e, session);
1375
+ }
1376
+
1377
+ static void loadAnnotationsFromEnvDumpDir(SpawnException &e, HandshakeSession &session) {
1378
+ TRACE_POINT();
1379
+ string path = session.envDumpDir + "/annotations";
1380
+ DIR *dir = opendir(path.c_str());
1381
+ if (dir == NULL) {
1382
+ return;
1383
+ }
1384
+
1385
+ ScopeGuard guard(boost::bind(doClosedir, dir));
1386
+ struct dirent *ent;
1387
+ while ((ent = readdir(dir)) != NULL) {
1388
+ if (ent->d_name[0] != '.') {
1389
+ e.setAnnotation(ent->d_name, strip(
1390
+ Passenger::readAll(path + "/" + ent->d_name)));
1391
+ }
1392
+ }
1393
+ }
1394
+
1395
+ void cleanup() {
1396
+ boost::this_thread::disable_interruption di;
1397
+ boost::this_thread::disable_syscall_interruption dsi;
1398
+ TRACE_POINT();
1399
+
1400
+ if (processExitWatcher != NULL) {
1401
+ processExitWatcher->interrupt_and_join();
1402
+ delete processExitWatcher;
1403
+ processExitWatcher = NULL;
1404
+ }
1405
+ if (finishSignalWatcher != NULL) {
1406
+ finishSignalWatcher->interrupt_and_join();
1407
+ delete finishSignalWatcher;
1408
+ finishSignalWatcher = NULL;
1409
+ }
1410
+ if (socketPingabilityWatcher != NULL) {
1411
+ socketPingabilityWatcher->interrupt_and_join();
1412
+ delete socketPingabilityWatcher;
1413
+ socketPingabilityWatcher = NULL;
1414
+ }
1415
+ if (stdoutAndErrCapturer != NULL) {
1416
+ stdoutAndErrCapturer->stop();
1417
+ }
1418
+ }
1419
+
1420
+ JourneyStep bestGuessSubprocessFailedStep() const {
1421
+ JourneyStep step = getFirstSubprocessJourneyStepWithState(STEP_IN_PROGRESS);
1422
+ if (step != UNKNOWN_JOURNEY_STEP) {
1423
+ return step;
1424
+ }
1425
+
1426
+ if (allSubprocessJourneyStepsHaveState(STEP_PERFORMED)) {
1427
+ return getLastSubprocessJourneyStepFrom(session.journey);
1428
+ } else {
1429
+ JourneyStep step = getLastSubprocessJourneyStepWithState(STEP_PERFORMED);
1430
+ if (step == UNKNOWN_JOURNEY_STEP) {
1431
+ return getFirstSubprocessJourneyStepFrom(session.journey);
1432
+ } else {
1433
+ assert(step != getLastSubprocessJourneyStepFrom(session.journey));
1434
+ return JourneyStep((int) step + 1);
1435
+ }
1436
+ }
1437
+ }
1438
+
1439
+ JourneyStep getFirstSubprocessJourneyStepFrom(const Journey &journey) const {
1440
+ JourneyStep firstStep = getFirstSubprocessJourneyStep();
1441
+ JourneyStep lastStep = getLastSubprocessJourneyStep();
1442
+ JourneyStep step;
1443
+
1444
+ for (step = firstStep; step <= lastStep; step = JourneyStep((int) step + 1)) {
1445
+ if (session.journey.hasStep(step)) {
1446
+ return step;
1447
+ }
1448
+ }
1449
+
1450
+ P_BUG("Never reached");
1451
+ return UNKNOWN_JOURNEY_STEP;
1452
+ }
1453
+
1454
+ JourneyStep getLastSubprocessJourneyStepFrom(const Journey &journey) const {
1455
+ JourneyStep firstStep = getFirstSubprocessJourneyStep();
1456
+ JourneyStep lastStep = getLastSubprocessJourneyStep();
1457
+ JourneyStep result = UNKNOWN_JOURNEY_STEP;
1458
+ JourneyStep step;
1459
+
1460
+ for (step = firstStep; step <= lastStep; step = JourneyStep((int) step + 1)) {
1461
+ if (session.journey.hasStep(step)) {
1462
+ result = step;
1463
+ }
1464
+ }
1465
+
1466
+ return result;
1467
+ }
1468
+
1469
+ bool allSubprocessJourneyStepsHaveState(JourneyStepState state) const {
1470
+ JourneyStep firstStep = getFirstSubprocessJourneyStep();
1471
+ JourneyStep lastStep = getLastSubprocessJourneyStep();
1472
+ JourneyStep step;
1473
+
1474
+ for (step = firstStep; step <= lastStep; step = JourneyStep((int) step + 1)) {
1475
+ if (!session.journey.hasStep(step)) {
1476
+ continue;
1477
+ }
1478
+
1479
+ if (session.journey.getStepInfo(step).state != state) {
1480
+ return false;
1481
+ }
1482
+ }
1483
+
1484
+ return true;
1485
+ }
1486
+
1487
+ JourneyStep getFirstSubprocessJourneyStepWithState(JourneyStepState state) const {
1488
+ JourneyStep firstStep = getFirstSubprocessJourneyStep();
1489
+ JourneyStep lastStep = getLastSubprocessJourneyStep();
1490
+ JourneyStep step;
1491
+
1492
+ for (step = firstStep; step <= lastStep; step = JourneyStep((int) step + 1)) {
1493
+ if (!session.journey.hasStep(step)) {
1494
+ continue;
1495
+ }
1496
+
1497
+ if (session.journey.getStepInfo(step).state == state) {
1498
+ return step;
1499
+ }
1500
+ }
1501
+
1502
+ return UNKNOWN_JOURNEY_STEP;
1503
+ }
1504
+
1505
+ JourneyStep getLastSubprocessJourneyStepWithState(JourneyStepState state) const {
1506
+ JourneyStep firstStep = getFirstSubprocessJourneyStep();
1507
+ JourneyStep lastStep = getLastSubprocessJourneyStep();
1508
+ JourneyStep step;
1509
+ JourneyStep result = UNKNOWN_JOURNEY_STEP;
1510
+
1511
+ for (step = firstStep; step <= lastStep; step = JourneyStep((int) step + 1)) {
1512
+ if (!session.journey.hasStep(step)) {
1513
+ continue;
1514
+ }
1515
+
1516
+ if (session.journey.getStepInfo(step).state == state) {
1517
+ result = step;
1518
+ }
1519
+ }
1520
+
1521
+ return result;
1522
+ }
1523
+
1524
+ public:
1525
+ struct DebugSupport {
1526
+ virtual ~DebugSupport() { }
1527
+ virtual void beginWaitUntilSpawningFinished() { }
1528
+ };
1529
+
1530
+ DebugSupport *debugSupport;
1531
+
1532
+
1533
+ HandshakePerform(HandshakeSession &_session, pid_t _pid,
1534
+ const FileDescriptor &_stdinFd = FileDescriptor(),
1535
+ const FileDescriptor &_stdoutAndErrFd = FileDescriptor(),
1536
+ const string &_alreadyReadStdoutAndErrData = string())
1537
+ : session(_session),
1538
+ config(session.config),
1539
+ pid(_pid),
1540
+ stdinFd(_stdinFd),
1541
+ stdoutAndErrFd(_stdoutAndErrFd),
1542
+ alreadyReadStdoutAndErrData(_alreadyReadStdoutAndErrData),
1543
+ processExitWatcher(NULL),
1544
+ finishSignalWatcher(NULL),
1545
+ processExited(false),
1546
+ finishState(NOT_FINISHED),
1547
+ socketPingabilityWatcher(NULL),
1548
+ socketIsNowPingable(false),
1549
+ debugSupport(NULL)
1550
+ {
1551
+ assert(_session.context != NULL);
1552
+ assert(_session.context->isFinalized());
1553
+ assert(_session.config != NULL);
1554
+ }
1555
+
1556
+ Result execute() {
1557
+ TRACE_POINT();
1558
+ ScopeGuard guard(boost::bind(&HandshakePerform::cleanup, this));
1559
+
1560
+ // We do not set SPAWNING_KIT_HANDSHAKE_PERFORM to the IN_PROGRESS or
1561
+ // PERFORMED state here. That will be done by the caller because
1562
+ // it may want to perform additional preparation.
1563
+
1564
+ try {
1565
+ initializeStdchannelsCapturing();
1566
+ startWatchingProcessExit();
1567
+ if (config->genericApp || config->findFreePort) {
1568
+ startWatchingSocketPingability();
1569
+ }
1570
+ if (!config->genericApp) {
1571
+ startWatchingFinishSignal();
1572
+ }
1573
+ } catch (const SpawnException &) {
1574
+ throw;
1575
+ } catch (const std::exception &originalException) {
1576
+ sleepShortlyToCaptureMoreStdoutStderr();
1577
+
1578
+ loadJourneyStateFromResponseDir();
1579
+ session.journey.setStepErrored(SPAWNING_KIT_HANDSHAKE_PERFORM);
1580
+
1581
+ SpawnException e(originalException, session.journey, config);
1582
+ e.setStdoutAndErrData(getStdoutErrData());
1583
+ e.setSubprocessPid(pid);
1584
+ throw e.finalize();
1585
+ }
1586
+
1587
+ UPDATE_TRACE_POINT();
1588
+ try {
1589
+ boost::unique_lock<boost::mutex> l(syncher);
1590
+ if (debugSupport != NULL) {
1591
+ debugSupport->beginWaitUntilSpawningFinished();
1592
+ }
1593
+ waitUntilSpawningFinished(l);
1594
+ Result result = handleResponse();
1595
+ loadJourneyStateFromResponseDir();
1596
+ return result;
1597
+ } catch (const SpawnException &) {
1598
+ throw;
1599
+ } catch (const std::exception &originalException) {
1600
+ sleepShortlyToCaptureMoreStdoutStderr();
1601
+
1602
+ loadJourneyStateFromResponseDir();
1603
+ session.journey.setStepErrored(SPAWNING_KIT_HANDSHAKE_PERFORM);
1604
+
1605
+ SpawnException e(originalException, session.journey, config);
1606
+ e.setSubprocessPid(pid);
1607
+ e.setStdoutAndErrData(getStdoutErrData());
1608
+ throw e.finalize();
1609
+ }
1610
+ }
1611
+
1612
+ static void loadJourneyStateFromResponseDir(HandshakeSession &session, pid_t pid,
1613
+ const BackgroundIOCapturerPtr &stdoutAndErrCapturer)
1614
+ {
1615
+ TRACE_POINT();
1616
+
1617
+ P_DEBUG("[App " << pid << " journey] Loading state from " << session.responseDir);
1618
+
1619
+ loadJourneyStateFromResponseDir(session, pid, stdoutAndErrCapturer,
1620
+ getFirstSubprocessJourneyStep(),
1621
+ getLastSubprocessJourneyStep());
1622
+
1623
+ UPDATE_TRACE_POINT();
1624
+ loadJourneyStateFromResponseDir(session, pid, stdoutAndErrCapturer,
1625
+ getFirstPreloaderJourneyStep(),
1626
+ // Also load state from PRELOADER_FINISH since the
1627
+ // preloader writes there.
1628
+ JourneyStep((int) getLastPreloaderJourneyStep() + 1));
1629
+ }
1630
+
1631
+ static void loadBasicInfoFromEnvDumpDir(const string &envDumpDir,
1632
+ string &envvars, string &userInfo, string &ulimits)
1633
+ {
1634
+ if (fileExists(envDumpDir + "/envvars")) {
1635
+ envvars = readAll(envDumpDir + "/envvars");
1636
+ }
1637
+ if (fileExists(envDumpDir + "/user_info")) {
1638
+ userInfo = readAll(envDumpDir + "/user_info");
1639
+ }
1640
+ if (fileExists(envDumpDir + "/ulimits")) {
1641
+ ulimits = readAll(envDumpDir + "/ulimits");
1642
+ }
1643
+ }
1644
+ };
1645
+
1646
+
1647
+ } // namespace SpawningKit
1648
+ } // namespace Passenger
1649
+
1650
+ #endif /* _PASSENGER_SPAWNING_KIT_HANDSHAKE_PERFORM_H_ */