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
@@ -34,6 +34,7 @@
34
34
  #include <boost/foreach.hpp>
35
35
  #include <oxt/thread.hpp>
36
36
  #include <oxt/backtrace.hpp>
37
+ #include <string>
37
38
  #include <vector>
38
39
 
39
40
  #include <sys/types.h>
@@ -43,7 +44,6 @@
43
44
  #include <LoggingKit/LoggingKit.h>
44
45
  #include <Utils.h>
45
46
  #include <Utils/StrIntUtils.h>
46
- #include <Core/SpawningKit/Config.h>
47
47
 
48
48
  namespace Passenger {
49
49
  namespace SpawningKit {
@@ -54,15 +54,15 @@ using namespace boost;
54
54
  /** A PipeWatcher lives until the file descriptor is closed. */
55
55
  class PipeWatcher: public boost::enable_shared_from_this<PipeWatcher> {
56
56
  private:
57
- ConfigPtr config;
58
57
  FileDescriptor fd;
59
- const char *name;
58
+ StaticString name;
59
+ string appGroupName;
60
+ string appLogFile;
60
61
  pid_t pid;
61
62
  bool started;
63
+ string logFile;
62
64
  boost::mutex startSyncher;
63
65
  boost::condition_variable startCond;
64
- const HashedStaticString appGroupName;
65
- const StaticString appLogFile;
66
66
 
67
67
  static void threadMain(boost::shared_ptr<PipeWatcher> self) {
68
68
  TRACE_POINT();
@@ -78,6 +78,16 @@ private:
78
78
  }
79
79
  }
80
80
 
81
+ UPDATE_TRACE_POINT();
82
+ FILE *f = NULL;
83
+ if (!logFile.empty()) {
84
+ f = fopen(logFile.c_str(), "a");
85
+ if (f == NULL) {
86
+ P_ERROR("Cannot open log file " << logFile);
87
+ return;
88
+ }
89
+ }
90
+
81
91
  UPDATE_TRACE_POINT();
82
92
  while (!boost::this_thread::interruption_requested()) {
83
93
  char buf[1024 * 8];
@@ -99,7 +109,7 @@ private:
99
109
  }
100
110
  } else if (ret == 1 && buf[0] == '\n') {
101
111
  UPDATE_TRACE_POINT();
102
- LoggingKit::logAppOutput(appGroupName, pid, name, "", 0, appLogFile);
112
+ printOrLogAppOutput(f, StaticString());
103
113
  } else {
104
114
  UPDATE_TRACE_POINT();
105
115
  vector<StaticString> lines;
@@ -109,29 +119,42 @@ private:
109
119
  }
110
120
  split(StaticString(buf, ret2), '\n', lines);
111
121
  foreach (const StaticString line, lines) {
112
- LoggingKit::logAppOutput(appGroupName, pid, name, line.data(), line.size(), appLogFile);
122
+ printOrLogAppOutput(f, line);
113
123
  }
114
124
  }
125
+ }
115
126
 
116
- if (config->outputHandler) {
117
- config->outputHandler(buf, ret);
118
- }
127
+ if (f != NULL) {
128
+ fclose(f);
129
+ }
130
+ }
131
+
132
+ void printOrLogAppOutput(FILE *f, const StaticString &line) {
133
+ if (f == NULL) {
134
+ LoggingKit::logAppOutput(appGroupName, pid, name, line.data(), line.size(), appLogFile);
135
+ } else {
136
+ fwrite(line.data(), 1, line.size(), f);
137
+ fwrite("\n", 1, 2, f);
138
+ fflush(f);
119
139
  }
120
140
  }
121
141
 
122
142
  public:
123
- PipeWatcher(const ConfigPtr &_config, const FileDescriptor &_fd,
124
- const char *_name, pid_t _pid,
125
- const HashedStaticString &_appGroupName, const StaticString &_appLogFile)
126
- : config(_config),
127
- fd(_fd),
143
+ PipeWatcher(const FileDescriptor &_fd, const StaticString &_name,
144
+ const string &_appGroupName, const string &_appLogFile,
145
+ pid_t _pid)
146
+ : fd(_fd),
128
147
  name(_name),
129
- pid(_pid),
130
- started(false),
131
148
  appGroupName(_appGroupName),
132
- appLogFile(_appLogFile)
149
+ appLogFile(_appLogFile),
150
+ pid(_pid),
151
+ started(false)
133
152
  { }
134
153
 
154
+ void setLogFile(const string &path) {
155
+ logFile = path;
156
+ }
157
+
135
158
  void initialize() {
136
159
  oxt::thread(boost::bind(threadMain, shared_from_this()),
137
160
  "PipeWatcher: PID " + toString(pid) + " " + name + ", fd " + toString(fd),
@@ -0,0 +1,534 @@
1
+ # About SpawningKit
2
+
3
+ SpawningKit is a subsystem that handles the spawning of web application processes in a reliable manner.
4
+
5
+ Spawning an application process is complex, involving many steps and with many failure scenarios. SpawningKit handles all this complexity while providing a simple interface that guarantees reliability.
6
+
7
+ Here is how SpawningKit is used. The caller supplies various parameters such as where the application is located, what language it's written in, what environment variables to apply, etc. SpawningKit then spawns the application process, checks whether the application spawned properly or whether it encountered an error, and then either returns an object that describes the resulting process or throws an exception that describes the failure.
8
+
9
+ Reliability and visibility are core features in SpawningKit. When SpawningKit returns, you know for sure whether the process started correctly or not. If the application did not start correctly, then the resulting exception describes the failure in a detailed enough manner that allows users to pinpoint the source of the problem. SpawningKit also enforces timeouts everywhere so that stuck processes are handled as well.
10
+
11
+ **Table of contents**:
12
+
13
+ * Important concepts and features
14
+ - Generic vs SpawningKit-enabled applications
15
+ - Wrappers
16
+ - Preloaders
17
+ - The start command
18
+ - Summary with examples
19
+ * API and implementation highlights
20
+ * Overview of the spawning journey
21
+ - When spawning a process without a preloader
22
+ - When starting a preloader
23
+ - When spawning a process through a preloader
24
+ - The Journey class
25
+ - The preparation and the HandshakePrepare class
26
+ - The handshake and the HandshakePerform class
27
+ - The SpawnEnvSetupper
28
+ * The work directory
29
+ - Structure
30
+ * Application response properties
31
+ * The preloader protocol
32
+ * Subprocess journey logging
33
+ * Error reporting
34
+ - Information sources
35
+ - How an error report is presented
36
+ - How supplied information affects error report generation
37
+ * Mechanism for waiting until the application is up
38
+
39
+ ---
40
+
41
+ ## Important concepts and features
42
+
43
+ ### Generic vs SpawningKit-enabled applications
44
+
45
+ SpawningKit can be used to spawn any web application, both those with and without explicit SpawningKit support.
46
+
47
+ When SpawningKit is used to spawn a generic application (without explicit SpawningKit support), the only requirement is that the application can be instructed to start and to listen on a specific TCP port on localhost. The user needs to specify a command string that tells SpawningKit how that is to be done. SpawningKit then looks for a free port that the application may use and executes the application using the supplied command string, telling it to listen on that specific port. (This approach is inspired by Heroku's Procfile system.) SpawningKit waits until the application is up by pinging the port. If the application fails (e.g. by terminating early or by not responding to pings in time) then SpawningKit will abort, reporting the application's stdout and stderr output.
48
+
49
+ Applications can also be modified with explicit SpawningKit support. Such applications can improve performance by telling SpawningKit that it wishes to listen on a Unix domain socket instead of a TCP socket; and they can provide more feedback about any spawning failures, such as with HTML-formatted error messages or by providing more information about where internally in the application or web framework the failure occurred.
50
+
51
+ ### Wrappers
52
+
53
+ In general, it is better if an application has explicit SpawningKit support, because then it is able to provide a nicer experience and better performance. But having to modify the application's code is a major hurdle.
54
+
55
+ Luckily, it is not always necessary to modify the application. Wrappers are small programs that aid in loading applications written in specific languages -- in particular interpreted languages because they allow modifying application behavior without requiring code modifications. When a wrapper is used, SpawningKit executes the wrapper, not the actual application. The wrapper loads the application and modifies its behavior in such a way that SpawningKit support is added (e.g. ability to report HTML-formatted errors), without requiring modifications to the application code.
56
+
57
+ Wrappers are only applicable to apps without explicit SpawningKit support.
58
+
59
+ Passenger comes with a few wrappers for specific languages, but SpawningKit itself is more generic and requires the caller to specify which wrapper to use (if at all).
60
+
61
+ For example, Ruby applications are typically spawned through the Passenger-supplied Ruby wrapper. The Ruby wrapper activates the Gemfile, loads the application, sets up a lightweight server that listens on a Unix domain socket, and reports this socket address back to SpawningKit.
62
+
63
+ ### Preloaders
64
+
65
+ Applications written in certain languages are able to save memory and to improve application startup time by using a technique called pre-forking. This works by starting an application process, and instead of using that process to handle requests, we use that process to fork (but not `exec()`) additional child processes that in turn are actually used for processing requests.
66
+
67
+ In SpawningKit terminology, we call the former a "preloader". Processes that actually handle requests (and these processes may either be forked from a preloader or be spawned directly without a preloader) usually do not have a specific name. But for the sake of clarity, let's call the latter -- within the context of this document only -- "worker processes".
68
+
69
+ Requests
70
+ |
71
+ |
72
+ \ /
73
+ .
74
+
75
+ +-----------+ +------------------+
76
+ | Preloader | --- forks many ---> | Worker processes |
77
+ +-----------+ +------------------+
78
+
79
+ Memory is saved because the preloader and its worker processes are able to share all memory that was already present in the preloader during forking, and that has not been modified in the worker processes. This concept is called Copy-on-Write (CoW) and works through the virtual memory system of modern operating systems.
80
+
81
+ For example, in Ruby applications a significant amount of memory is taken up by the bytecode representation of dependent libraries (e.g. the Rails framework). Loading all the dependent libraries typically takes time in the order of many seconds. By using a preloader to fork worker processes (instead of starting the worker processes without a preloader), all worker processes can share the memory taken up by the dependent libraries, as well as the application code itself and possibly any resources that the preloder loaded (e.g. a geo-IP database loaded from a file). Forking a worker process from a preloader is also extremely fast, in the order of milliseconds -- much faster than starting a worker processes without a preloader.
82
+
83
+ SpawningKit provides facilities to use this preforking technique. Obviously, this technique can only be used if the target programming language actually supports forking. This is the case with e.g. C, Ruby (using MRI) and Python (using CPython), but not with e.g. Node.js, Ruby (using JRuby), Go and anything running on the JVM.
84
+
85
+ Using the preforking technique through SpawningKit requires either application code modifications, or the existance of a wrapper that supports this technique.
86
+
87
+ ### The start command
88
+
89
+ Regardless of whether SpawningKit is used to spawn an application with or without explicit SpawningKit support, and regardless of whether a wrapper is used and whether the application/wrapper can function as a preloader, SpawningKit asks the caller to supply a "start command" that tells it how to execute the wrapper or the application. SpawningKit then uses the handshaking procedure (see: "Overview of the spawning journey") to communicate with the wrapper/application whether it should start in preloader mode or not.
90
+
91
+ ### Summary with examples
92
+
93
+ To help you better understand the concepts, the following summarizes some of the above concepts and how they map to supportable languages.
94
+
95
+ SpawningKit-enabled wrappers are included in Passenger for these languages: Ruby, Python, Node.js, Meteor, and Perl.
96
+
97
+ Any existing app that accepts http requests can be used by writing a wrapper, and any new app can be written with SpawningKit compatibility to avoid the need for a wrapper.
98
+
99
+
100
+ ## API and implementation highlights
101
+
102
+ ### Context
103
+
104
+ Before using SpawningKit, one must create a Context object. A Context contains global state, global configuration and references dependencies that SpawningKit needs.
105
+
106
+ Create a Context object, set some initial configuration, inject some dependencies, then call `finalize()`.
107
+
108
+ ~~~c++
109
+ SpawningKit::Context::Schema schema;
110
+ SpawningKit::Context context(schema);
111
+
112
+ context.resourceLocator = ...;
113
+ context.integrationMode = "standalone";
114
+ context.finalize();
115
+ ~~~
116
+
117
+ ### Spawners
118
+
119
+ Use Spawners to spawn application processes. There are two main types of Spawners:
120
+
121
+ * DirectSpawner, which corresponds to `PassengerSpawnMethod direct`.
122
+ * SmartSpawner, which corresponds to `PassengerSpawnMethod smart`.
123
+
124
+ See also [the background documentation on spawn methods](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/) and the "Preloaders" section in this README.
125
+
126
+ You can create Spawners directly, but it's recommended to use a Factory instead. A Factory accepts an ApplicationPool::Options object, and depending on `options.spawnMethod`, creates either a DirectSpawner or a SmartSpawner.
127
+
128
+ Once you have obtained a Spawner object, call `spawn(options)` which spawns an application process. It returns a `Result` object.
129
+
130
+ ~~~c++
131
+ ApplicationPool::options;
132
+
133
+ options.appRoot = "/foo/bar/public";
134
+ options.appType = "rack";
135
+ options.spawnMethod = "smart";
136
+ ...
137
+
138
+ SpawningKit::Factory factory(&context);
139
+ SpawningKit::SpawnerPtr spawner = factory->create(options);
140
+
141
+ SpawningKit::Result result = spawner->spawn(options);
142
+ P_WARN("Application process spawned, PID is " << result.pid);
143
+ ~~~
144
+
145
+ There is also a DummySpawner class, which is only used during unit tests.
146
+
147
+ ### HandshakePrepare and HandshakePerform
148
+
149
+ Inside SmartSpawner and DirectSpawner, HandshakePrepare and HandshakePerform are used to perform a lot of the heavy lifting. See "Overview of the spawning journey" -- HandshakePrepare and HandshakePerform are responsible for most of the stuff described there.
150
+
151
+ In fact, DirectSpawner is just a thin wrapper around HandshakePrepare and HandshakePerform. It first runs HandshakePrepare, then forks a subprocess that executes SpawnEnvSetupper, and then (in the parent process) runs HandshakePerform.
152
+
153
+ SmartSpawner is a bit bigger because it needs to implement the whole preloading mechanism (see section "Preloaders"), but it still uses HandshakePrepare and HandshakePerform to spawn the preloader, and to negotiate with the subprocess created by the preloader.
154
+
155
+ Here are some simplified interaction diagrams.
156
+
157
+ ### Configuration object
158
+
159
+ HandshakePrepare and HandshakePerform do not accept an ApplicationPool::Options object, but a SpawningKit::Config object. It contains the configuration that HandshakePrepare/Perform need to perform a single spawn. SmartSpawner and DirectSpawner internally convert an ApplicationPool::Options into a SpawningKit::Config.
160
+
161
+ You can see this at work in `DirectSpawner::setConfigFromAppPoolOptions()` and `SmartSpawner::setConfigFromAppPoolOptions()`.
162
+
163
+ ### Exception object
164
+
165
+ If HandshakePrepare, HandshakePerform, or `Spawner::spawn()` fails, then they will throw a SpawningKit::SpawnException object. Learn more about what this exception represents in section "Error reporting".
166
+
167
+ ### Journey
168
+
169
+ The aforementioned exception object contains a SpawningKit::Journey object which describes how the spawning journey looked like and where in the journey something failed. It can be obtained through `exception.getJourney()`. Learn more about journeys in section "Overview of the spawning journey".
170
+
171
+ ### Error renderer
172
+
173
+ The ErrorRenderer class helps you render an error page, both one with and one without details. It uses the assets in `resources/templates/error_page`.
174
+
175
+ ## Overview of the spawning journey
176
+
177
+ Spawning a process can take one of three routes:
178
+
179
+ * If we are spawning a worker process, then it is either (1) spawned through a preloader, or (2) it isn't.
180
+ * We may also (3) spawn the application as a preloader instead of as a worker.
181
+
182
+ We refer to the walking of this route (performing all the steps involved in a route) a "journey". Below follows an overview of the routes.
183
+
184
+ In the following descriptions, "(In SpawningKit)" refers to the process that runs SpawningKit, which is typically the Passenger Core.
185
+
186
+ ### When spawning a process without a preloader
187
+
188
+ The journey looks like this when no preloader is used:
189
+
190
+ (In SpawningKit) (In subprocess)
191
+
192
+ Preparation
193
+ |
194
+ Fork subprocess --------------> Before first exec
195
+ | |
196
+ Handshake Execute SpawnEnvSetupper to setup spawn env (--before)
197
+ | |
198
+ Finish Load OS shell (if option enabled)
199
+ |
200
+ Execute SpawnEnvSetupper (--after)
201
+ |
202
+ Execute wrapper (if applicable)
203
+ |
204
+ Execute/load app
205
+ |
206
+ Start listening
207
+ |
208
+ Finish
209
+
210
+ ### When starting a preloader
211
+
212
+ The journey looks like this when starting a preloader:
213
+
214
+ (In SpawningKit) (In subprocess)
215
+
216
+ Preparation
217
+ |
218
+ Fork preloader --------------> Before first exec
219
+ | |
220
+ Handshake Execute SpawnEnvSetupper to setup spawn env (--before)
221
+ | |
222
+ Finish Load OS shell (if option enabled)
223
+ |
224
+ Execute SpawnEnvSetupper (--after)
225
+ |
226
+ Execute wrapper in preloader mode
227
+ (if applicable)
228
+ |
229
+ Load application
230
+ (and if applicable, do so in
231
+ preloader mode)
232
+ |
233
+ Start listening for commands
234
+ |
235
+ Finish
236
+
237
+ ### When spawning a process through a preloader
238
+
239
+ The journey looks like this when using a preloader to spawn a process:
240
+
241
+ (In SpawningKit) (In preloader) (In subprocess)
242
+
243
+ Preparation
244
+ |
245
+ Tell preloader to spawn ------> Preparation
246
+ | |
247
+ Receive, process Fork ----------------> Preparation
248
+ preloader response | |
249
+ | Send response Start listening
250
+ Handshake | |
251
+ | Finish Finish
252
+ Finish
253
+
254
+ ### The Journey class
255
+
256
+ The Journey class represents a journey. It records all the steps taken so far, which steps haven't been taken yet, at which step we failed, and how long each step took.
257
+
258
+ A journey consists of a few parallel actors, with each actor having multiple steps and the ability to invoke another actor. Each step can be in one of three states:
259
+
260
+ * Step has not started yet. Inside error pages, this will be visualized with an empty placeholder.
261
+ * Step is currently in progress. Will be visualized with a spinner.
262
+ * Step has already been performed successfully. Will be visualized with a green tick.
263
+ * Step has failed. Will be visualized with a red mark.
264
+
265
+ Steps that have performed successfully or failed also have an associated begin time. The duration of each step is inferred from the begin time of that step, vs either the end time of that step or (if available) the begin time of the next step.
266
+
267
+ ### The preparation and the HandshakePrepare class
268
+
269
+ Inside the process running SpawningKit, before forking a subprocess (regardless of whether that is going to be a preloader or a worker), various preparation needs to be done. This preparation work is implemented in Handshake/Prepare.h, in the HandshakePrepare class.
270
+
271
+ Here is a list of the work involved in preparation:
272
+
273
+ * Creating a temporary directory for the purpose of performing a handshake with the subprocess. This directory is called a "work directory". Learn more in sections "The handshake and the HandshakePerform class" and "The work directory".
274
+
275
+ **Note**: the "work directory" in this context refers to this directory, not to the Unix concept of current working directory (`getpwd()`). This directory also has got nothing to do with the instance directory that Passenger uses to keep track of running Passenger instances; that is a separate concept and mechanism.
276
+ * If the application is not SpawningKit-enabled, or if the caller explicitly instructed so, HandshakePrepare finds a free port for the worker process to listen on. This port number will be passed to the worker process.
277
+ * Dumping, into the work directory, important information that the subprocess should know of. For example: whether it's going to be started in development or production mode, the process title to assume, etc. This information is called the _spawn arguments_.
278
+ * Calculating which exact arguments need to be passed to the `exec()` call. Because it's unsafe to do this after forking.
279
+
280
+ ### The handshake and the HandshakePerform class
281
+
282
+ Once a process (whether preloader or worker) is spawned, SpawningKit needs to wait until it's up. If the spawning failed for whatever reason, then SpawningKit needs to infer that reason from information that the subprocess may have dumped into the work directory, and from the stdout/stderr output.
283
+
284
+ This is implemented in Handshake/Perform.h, in the HandshakePerform class.
285
+
286
+ ### The SpawnEnvSetupper
287
+
288
+ The first thing the subprocess does is execute the SpawnEnvSetupper (which is contained inside PassengerAgent and can be invoked through a specific argument). This program performs various basic preparation in the subprocess such as:
289
+
290
+ * Changing the current working directory to that of the application.
291
+ * Setting environment variables, ulimits, etc.
292
+ * Changing the UID and GID of the process.
293
+
294
+ It does all this by reading arguments from the work directory (see: "The work directory").
295
+
296
+ The reason why this program exists is because all this work is unsafe to do inside the process that runs SpawningKit. Because after a `fork()`, one is only allowed to call async-signal-safe code. That means no memory allocations, or even calling `setenv()`.
297
+
298
+ You can see in the diagrams that SpawnEnvSetupper is called twice, once before and once after loading the OS shell. The OS shell could arbitrarily change the environment (environment variables, ulimits, current working directory, etc.), sometimes without the user knowing about this. The main job that the SpawnEnvSetupper performs after the OS shell, is restoring some of the environment that the SpawningKit caller requested (e.g. specific environment variables, ulimits), as well as dumping the entire environment to the work directory so that the user can debug things when something is wrong.
299
+
300
+ The SpawnEnvSetupper is implemented in SpawnEnvSetupperMain.cpp.
301
+
302
+ ## The work directory
303
+
304
+ The work directory is a temporary directory created at the very beginning of the spawning journey, during the SpawningKit preparation step. Note that this "work directory" is distinct from the Unix concept of current working directory (`getpwd()`).
305
+
306
+ The work directory's purpose is to:
307
+
308
+ 1. ...store information about the spawning procedure that the subprocess should know (the _spawn arguments_).
309
+ 2. ...receive information from the subprocess about how spawning went (the _response_). For example the subprocess can use it to signal.
310
+ 3. ...receive information about the subprocess's environment, so that this information can be displayed to the user for debugging purposes in case something goes wrong.
311
+
312
+ Here, "subprocess" does not only refer to the worker process, but also to the SpawnEnvSetupper, the wrapper (if applicable) and even the shell. All of these can (not not necessarily *have to*) make use of the work directory. For example, the SpawnEnvSetupper dumps environment information into the work directory. Some wrappers may also dump environment information.
313
+
314
+ The work directory's location is communicated to subprocesses through the `PASSENGER_SPAWN_WORK_DIR` environment variable.
315
+
316
+ ### Structure
317
+
318
+ The work directory has the following structure. Entries that are created during the SpawningKit preparation step are marked with "[P]". All other entries may be created by the subprocess.
319
+
320
+ ~~~
321
+ Work directory
322
+ |
323
+ +-- args.json [P]
324
+ |
325
+ +-- args/ [P]
326
+ | |
327
+ | +-- app_root [P]
328
+ | +-- log_level [P]
329
+ | +-- ...etc... [P]
330
+ |
331
+ +-- stdin [P] (only when spawning through a preloader)
332
+ +-- stdout_and_err [P] (only when spawning through a preloader)
333
+ |
334
+ +-- response/ [P]
335
+ | |
336
+ | +-- finish [P]
337
+ | |
338
+ | +-- properties.json
339
+ | |
340
+ | +-- error/ [P]
341
+ | | |
342
+ | | +-- category
343
+ | | |
344
+ | | +-- summary
345
+ | | |
346
+ | | +-- problem_description.txt
347
+ | | +-- problem_description.html
348
+ | | |
349
+ | | +-- advanced_problem_details
350
+ | | |
351
+ | | +-- solution_description.txt
352
+ | | +-- solution_description.html
353
+ | |
354
+ | +-- steps/ [P]
355
+ | |
356
+ | +-- subprocess_spawn_env_setupper_before_shell/ [P]
357
+ | | |
358
+ | | +-- state
359
+ | | |
360
+ | | +-- begin_time
361
+ | | | -OR-
362
+ | | | begin_time_monotonic
363
+ | | |
364
+ | | +-- end_time
365
+ | | -OR-
366
+ | | end_time_monotonic
367
+ | |
368
+ | +-- ...
369
+ | |
370
+ | +-- subprocess_listen/ [P]
371
+ | |
372
+ | +-- state
373
+ | |
374
+ | +-- begin_time
375
+ | | -OR-
376
+ | | begin_time_monotonic
377
+ | |
378
+ | +-- end_time
379
+ | -OR-
380
+ | end_time_monotonic
381
+ |
382
+ +-- envdump/ [P]
383
+ |
384
+ +-- envvars
385
+ |
386
+ +-- user_info
387
+ |
388
+ +-- ulimits
389
+ |
390
+ +-- annotations/ [P]
391
+ |
392
+ +-- some name
393
+ |
394
+ +-- some other name
395
+ |
396
+ +-- ...
397
+
398
+ ~~~
399
+
400
+ There are two entries representing the spawn arguments:
401
+
402
+ * `args.json` is a JSON file containing the arguments.
403
+
404
+ ~~~
405
+ { "app_root": "/path-to-app", "log_level": 3, ... }
406
+ ~~~
407
+
408
+ * `args/` is a directory containing the arguments. Inside this directory there are files, with each file representing a single argument. This directory provides an alternative way for subprocesses to read the arguments, which is convenient for subprocesses that don't have easy access to a JSON parser (e.g. Bash).
409
+
410
+ The `response/` directory represents the response:
411
+
412
+ * `finish` is a FIFO file. If a wrapper is used, or if the application has explicit support for SpawningKit, then either of them can write to this FIFO file to indicate that it has done spawning. See "Mechanism for waiting until the application is up" for more information.
413
+ * If a wrapper is used, or if the application has explicit support for SpawningKit, then one of them may create a `properties.json` in order to communicate back to SpawningKit information about the spawned worker process. For example, if the application process started listening on a random port, then this file can be used to tell SpawningKit which port the process is listening on. See "Application response properties" for more information.
414
+ * `stdin` and `stdout_and_err` are FIFO files. They are only created (by the preloader) when using a preloader to spawn a new worker process. These FIFOs refer to the spawned worker process's stdin, stdout and stderr.
415
+ * If the subprocess fails, then it can communicate back specific error messages through the `error/` directory. See "Error reporting" (especially "Information sources") for more information.
416
+ * The subprocess must regularly update the contents of the `steps/` directory to allow SpawningKit to know which step in the journey the subprocess is executing, and what the state and and begin time of each step is. See "Subprocess journey logging" for more information.
417
+
418
+ The subprocess should dump information about its environment into the `envdump/` directory. Information includes environment variables (`envvars`), ulimits (`ulimits`), UID/GID (`user_info`), and anything else that the subprocess deems relevant (`annotations/`). If spawning fails, then the information reported in this directory will be included in the error report (see "Error reporting").
419
+
420
+ ## Application response properties
421
+
422
+ If a wrapper is used or if the application has explicit support for SpawningKit, then either of them may create a `properties.json` inside the `response/` subdirectory of the work directory, in order to communicate back to SpawningKit information about the spawned application process. For example, if the application process started listening on a random port, then this file can be used to tell SpawningKit which port the process is listening on.
423
+
424
+ `properties.json` may only contain the following keys:
425
+
426
+ * `sockets` -- an array objects describing the sockets on which the application listens for requests. The format is as follows. All fields are required unless otherwise specified.
427
+
428
+ [
429
+ {
430
+ "address": "tcp://127.0.0.1:1234" | "unix:/path-to-unix-socket",
431
+ "protocol": "http" | "session" | "preloader" | "arbitrary-other-value",
432
+ "concurrency": <integer>,
433
+ "accept_http_requests": true | false, // optional; default: false
434
+ "description": "description of this socket" // optional
435
+ },
436
+ ...
437
+ ]
438
+
439
+ The `address` field describes the address of the socket, which is either a TCP address or a Unix domain socket path. In case of the latter, the Unix domain socket **must** have the same file owner as the application process.
440
+
441
+ The `protocol` field describes the protocol that this socket speaks. The value "http" is obvious; the value "session" refers to an internal SCGI-ish protocol that the Ruby and Python wrappers speak with Passenger. The value "preloader" means that this socket is used for receiving preloader commands (only preloaders are supposed to report such sockets; see "The preloader protocol"). Other arbitrary values are also allowed.
442
+
443
+ The `concurrency` field describes how many concurrent requests this socket can handle. The special value 0 means unlimited.
444
+
445
+ If the spawned process is a worker process (i.e. not a preloader process) then there must be at least one socket for which `accept_http_requests` is set to true. This field tells Passenger that HTTP traffic may be forwarded to this particular socket. You may wonder: why does this exist? Isn't it already enough if the application reports at least one socket that speaks the "http" protocol? The answer is no: whether Passenger should forward HTTP traffic to a specific socket has got nothing to do with whether that socket speaks HTTP. For example Passenger forwards HTTP traffic to the Ruby and Python wrappers using the "session" protocol. Furthermore, the Ruby wrapper spawns an HTTP socket, but it's for debugging purposes only and is slow, and so it should not be used for receiving live HTTP traffic. Note that a socket with `accept_http_requests` set to true **must** speak either the "http" or the "session" protocol. Other protocols are not allowed.
446
+
447
+ The `description` field may be used in the future to display additional information about an application process, for example inside admin tools, but currently it is not used.
448
+
449
+ ## The preloader protocol
450
+
451
+ The "Tell preloader to spawn" and "Receive, process preloader response" steps in the spawn journey work as follows.
452
+
453
+ Upon starting the preloader, the preloader listens for commands on a Unix domain socket. SpawningKit tells the preloader to spawn a worker process by sending a command over the socket. The command is a JSON document on a single line:
454
+
455
+ ~~~json
456
+ { "command": "spawn", "work_dir": "/path-to-work-dir" }
457
+ ~~~
458
+
459
+ The preloader then forks a child process, and (before the next step in the journey is performed) immediately responds with either a success or an error response:
460
+
461
+ ~~~json
462
+ { "result": "ok", "pid": 1234 }
463
+ { "result": "error", "message": "something went wrong" }
464
+ ~~~
465
+
466
+ The worker process's stdin, stdout and stderr are stored in FIFO files inside the work directory. SpawningKit then opens these FIFOs and proceeds with handshaking with the worker process.
467
+
468
+ ## Subprocess journey logging
469
+
470
+ It is the Passenger Core (running SpawningKit) that initiates a spawning journey and that reports errors to users. Some steps in the journey are performed by actors that are not the Passenger Core (e.g. the preloader and the subprocess). How do these actors communicate to the SpawningKit code running inside the Passenger Core about the state of *their* part of the journey?
471
+
472
+ This is done through the `steps/` subdirectory in the work directory. Subprocesses write to files in `steps/` regularly. Before SpawningKit's code returns, it loads information from `steps`/ and loads these into the journey object.
473
+
474
+ Each subdirectory in `steps/` represents a step in the part of the journey that a subprocess is responsible for. The files inside such a subdirectory communicate the state of that step:
475
+
476
+ * `state` must contain one of `STEP_NOT_STARTED`, `STEP_IN_PROGRESS`, `STEP_PERFORMED` or `STEP_ERRORED`.
477
+ * Either `begin_time` or `begin_time_monotonic` (the latter is preferred) must exist.
478
+
479
+ `begin_time` must contain a Unix timestamp representing the wall clock time at which this step began. The number may be a floating point number for sub-second precision.
480
+
481
+ `begin_time_monotonic` is the same, but must contain a timestamp obtained from the monotonic clock instead of the wall clock. It is recommended that subprocesses create this file, not `begin_time`, because the monotonic clock is not influenced by clock skews (e.g. daylight savings, time zone changes or NTP updates).
482
+
483
+ Because not all programming languages allow access to the monotonic clock, SpawningKit allows both mechanisms.
484
+ * Either `end_time` or `end_time_monotonic` (the latter is preferred) must exist.
485
+
486
+ The purpose of these files are anologous to `begin_time`/`begin_time_monotonic`, but instead record the time at which this step ended.
487
+
488
+ ## Error reporting
489
+
490
+ When something goes wrong during spawning, SpawningKit generates an error report. This report contains all the details you need to pinpoint the source of the problem, and is represented by the SpawnException class.
491
+
492
+ A report contains the following information:
493
+
494
+ * A broad **category** in which the problem belongs. Is it an internal program error? An operating system (system call) error? A filesystem error? An I/O error?
495
+ * A **summary** of the problem. This typically consists of a single line line and is in plain text format.
496
+ * A detailed **problem description**, in HTML format. This is a longer piece of narrative that is typically structured in two parts: a high-level description of the problem, meant for beginners; as well as a **advanced problem details** part that aids debugging. For example, if a file system permission error was encountered, the high-level description could explain what a file system permission is and that it's not Passenger's fault. The advanced information part could display the filename in question, as well as the OS error code and OS error message.
497
+ * A detailed **solution description**, in HTML format. This is a longer piece of narrative that explains in a detailed manner how to solve the problem.
498
+ * Various **auxiliary details** which are not directly related to the error, but may be useful or necessary for the purpose of debugging the problem: stdout and stderr output so far; ulimits; environment variables; UID, GID of the process in which the error occurred; system metrics such as CPU and RAM; etcetera.
499
+ * A description of the **journey**: which steps inside the journey have been performed, are in progress, or have failed; and how long each step took.
500
+
501
+ ### Information sources
502
+
503
+ SpawningKit generates an error report (a SpawnException object) by gathering information from multiple sources. One source is SpawningKit itself: if something goes wrong within the preparation step for example, then SpawningKit knows that the error originated from there, and so it will only use internal information to generate an error report.
504
+
505
+ The other source is the subprocess. If something goes wrong during the handshake step, then true source of the problem can either be in SpawningKit itself (e.g. it ran out of file descriptors), or in the subprocess (e.g. the subprocess encountered a filesystem permission error). Subprocesses can communicate to SpawningKit about errors that they have encountered by dumping information into the `response/error/` subdirectory of the work directory. SpawningKit will use this information in generating an error report.
506
+
507
+ ### How an error report is presented
508
+
509
+ The error report is presented in two ways:
510
+
511
+ 1. In the terminal or in log files.
512
+ 2. In an HTML page.
513
+
514
+ The summary is only meant to be displayed in the terminal or in the log files as a one-liner. It can contain basic details (such as the OS error code) but is not meant to contain finer details such as the subprocess stdout/stderr output, the environment variable dump, etc.
515
+
516
+ Everything else is meant to be displayed in an HTML page. The HTML page explicitly does not include the summary, so the summary must not contain any information that isn't available in all the other fields.
517
+
518
+ The advanced problem details are only displayed in the HTML page if one does not explicitly supply a problem description. See "Generating an error report" for more information.
519
+
520
+ ### How supplied information affects error report generation
521
+
522
+ Only a _category_ and a _journey description_ are required for generating an error report. The SpawnException class is capable of automatically generating an appropriate (albeit generic) summary, problem description and solution description based on the category and which step in the journey failed.
523
+
524
+ If one doesn't supply a problem description, but does supply advanced problem details, then the automatically-generated problem description will include the advanced problem details. The advanced problem details aren't used in any other way, so if one does supply a problem description then one must take care of including the advanced problem details.
525
+
526
+ Inside the SpawningKit codebase, an error report is generated by creating a SpawnException object. Subprocesses such as the preloader, the SpawnEnvSetupper, the wrapper and the app, can aid in generating the error report by providing their own details through the `error/` and `envdump/` subdirectories inside the work directory. In particular: subprocesses can provide the problem description and the solution description in one of two formats: either plain-text or HTML. So e.g. only one of `problem_description.txt` or `problem_description.html` need to exist, not both.
527
+
528
+ ## Mechanism for waiting until the application is up
529
+
530
+ SpawningKit utilizes two mechanisms to wait until the application is up. It invokes both mechanisms at the same time and waits until at least one succeeds.
531
+
532
+ The first mechanism the `response/finish` file in the work directory. A wrapper or a SpawningKit-enabled application can write to that file to tell SpawningKit that it is done, either successfully (by writing `1`) or with an error (by writing `0`).
533
+
534
+ The second mechanism is only activated when the caller has told SpawningKit that the application is generic, or when the caller has told SpawningKit to find a free port for the application. In this case, SpawningKit will wait until the port that it has found can be connected to.