passenger 5.0.0.beta3 → 5.0.0.rc1

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 (218) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/.editorconfig +11 -5
  5. data/CHANGELOG +38 -0
  6. data/CONTRIBUTING.md +1 -4
  7. data/Gemfile +0 -1
  8. data/Gemfile.lock +0 -2
  9. data/Rakefile +33 -33
  10. data/bin/passenger +1 -1
  11. data/bin/passenger-config +1 -1
  12. data/bin/passenger-install-apache2-module +800 -800
  13. data/bin/passenger-install-nginx-module +592 -592
  14. data/bin/passenger-memory-stats +127 -127
  15. data/bin/passenger-status +216 -216
  16. data/build/agents.rb +127 -127
  17. data/build/apache2.rb +87 -87
  18. data/build/basics.rb +60 -60
  19. data/build/common_library.rb +165 -165
  20. data/build/cplusplus_support.rb +51 -51
  21. data/build/cxx_tests.rb +268 -268
  22. data/build/debian.rb +143 -143
  23. data/build/documentation.rb +58 -58
  24. data/build/integration_tests.rb +81 -81
  25. data/build/misc.rb +132 -132
  26. data/build/nginx.rb +20 -20
  27. data/build/node_tests.rb +7 -7
  28. data/build/oxt_tests.rb +14 -14
  29. data/build/packaging.rb +570 -570
  30. data/build/preprocessor.rb +260 -260
  31. data/build/rake_extensions.rb +71 -71
  32. data/build/ruby_extension.rb +29 -29
  33. data/build/ruby_tests.rb +6 -6
  34. data/build/test_basics.rb +37 -37
  35. data/debian.template/control.template +3 -5
  36. data/dev/copy_boost_headers +134 -134
  37. data/dev/install_scripts_bootstrap_code.rb +25 -25
  38. data/dev/list_tests +20 -20
  39. data/dev/ruby_server.rb +223 -223
  40. data/dev/runner +18 -18
  41. data/doc/ServerOptimizationGuide.txt.md +55 -2
  42. data/doc/Users guide Nginx.txt +0 -26
  43. data/doc/Users guide Standalone.txt +5 -1
  44. data/doc/users_guide_snippets/tips.txt +9 -0
  45. data/ext/common/ApplicationPool2/Group.h +23 -11
  46. data/ext/common/ApplicationPool2/Implementation.cpp +32 -7
  47. data/ext/common/ApplicationPool2/Pool.h +22 -17
  48. data/ext/common/ApplicationPool2/SmartSpawner.h +4 -1
  49. data/ext/common/ApplicationPool2/Spawner.h +1 -1
  50. data/ext/common/Constants.h +1 -1
  51. data/ext/common/agents/Base.cpp +35 -20
  52. data/ext/common/agents/HelperAgent/Main.cpp +8 -1
  53. data/ext/common/agents/HelperAgent/OptionParser.h +18 -4
  54. data/ext/common/agents/HelperAgent/RequestHandler.h +2 -83
  55. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +54 -1
  56. data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +7 -4
  57. data/ext/common/agents/Main.cpp +1 -1
  58. data/ext/common/agents/Watchdog/Main.cpp +54 -19
  59. data/ext/nginx/Configuration.c +7 -0
  60. data/ext/nginx/ContentHandler.c +9 -1
  61. data/helper-scripts/backtrace-sanitizer.rb +106 -87
  62. data/helper-scripts/crash-watch.rb +32 -0
  63. data/helper-scripts/download_binaries/extconf.rb +38 -38
  64. data/helper-scripts/meteor-loader.rb +107 -107
  65. data/helper-scripts/prespawn +101 -101
  66. data/helper-scripts/rack-loader.rb +96 -96
  67. data/helper-scripts/rack-preloader.rb +137 -137
  68. data/lib/phusion_passenger.rb +292 -292
  69. data/lib/phusion_passenger/abstract_installer.rb +438 -438
  70. data/lib/phusion_passenger/active_support3_extensions/init.rb +168 -170
  71. data/lib/phusion_passenger/admin_tools.rb +20 -20
  72. data/lib/phusion_passenger/admin_tools/instance.rb +178 -178
  73. data/lib/phusion_passenger/admin_tools/instance_registry.rb +61 -61
  74. data/lib/phusion_passenger/admin_tools/memory_stats.rb +267 -267
  75. data/lib/phusion_passenger/apache2/config_options.rb +182 -182
  76. data/lib/phusion_passenger/common_library.rb +479 -485
  77. data/lib/phusion_passenger/config/about_command.rb +161 -161
  78. data/lib/phusion_passenger/config/admin_command_command.rb +129 -129
  79. data/lib/phusion_passenger/config/agent_compiler.rb +121 -121
  80. data/lib/phusion_passenger/config/build_native_support_command.rb +43 -43
  81. data/lib/phusion_passenger/config/command.rb +25 -25
  82. data/lib/phusion_passenger/config/compile_agent_command.rb +62 -62
  83. data/lib/phusion_passenger/config/compile_nginx_engine_command.rb +88 -73
  84. data/lib/phusion_passenger/config/detach_process_command.rb +72 -72
  85. data/lib/phusion_passenger/config/download_agent_command.rb +246 -227
  86. data/lib/phusion_passenger/config/download_nginx_engine_command.rb +245 -224
  87. data/lib/phusion_passenger/config/install_agent_command.rb +144 -132
  88. data/lib/phusion_passenger/config/install_standalone_runtime_command.rb +205 -185
  89. data/lib/phusion_passenger/config/installation_utils.rb +204 -204
  90. data/lib/phusion_passenger/config/list_instances_command.rb +64 -64
  91. data/lib/phusion_passenger/config/main.rb +152 -152
  92. data/lib/phusion_passenger/config/nginx_engine_compiler.rb +319 -300
  93. data/lib/phusion_passenger/config/reopen_logs_command.rb +67 -67
  94. data/lib/phusion_passenger/config/restart_app_command.rb +155 -155
  95. data/lib/phusion_passenger/config/system_metrics_command.rb +13 -13
  96. data/lib/phusion_passenger/config/utils.rb +95 -95
  97. data/lib/phusion_passenger/config/validate_install_command.rb +198 -198
  98. data/lib/phusion_passenger/console_text_template.rb +25 -25
  99. data/lib/phusion_passenger/constants.rb +90 -90
  100. data/lib/phusion_passenger/debug_logging.rb +106 -106
  101. data/lib/phusion_passenger/loader_shared_helpers.rb +447 -432
  102. data/lib/phusion_passenger/message_channel.rb +312 -312
  103. data/lib/phusion_passenger/message_client.rb +176 -176
  104. data/lib/phusion_passenger/native_support.rb +369 -369
  105. data/lib/phusion_passenger/nginx/config_options.rb +297 -297
  106. data/lib/phusion_passenger/packaging.rb +131 -131
  107. data/lib/phusion_passenger/platform_info.rb +360 -360
  108. data/lib/phusion_passenger/platform_info/apache.rb +767 -767
  109. data/lib/phusion_passenger/platform_info/apache_detector.rb +199 -199
  110. data/lib/phusion_passenger/platform_info/binary_compatibility.rb +107 -107
  111. data/lib/phusion_passenger/platform_info/compiler.rb +570 -570
  112. data/lib/phusion_passenger/platform_info/curl.rb +32 -32
  113. data/lib/phusion_passenger/platform_info/cxx_portability.rb +188 -188
  114. data/lib/phusion_passenger/platform_info/depcheck.rb +372 -372
  115. data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +109 -109
  116. data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +4 -4
  117. data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +10 -34
  118. data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +101 -101
  119. data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +5 -5
  120. data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +13 -13
  121. data/lib/phusion_passenger/platform_info/linux.rb +55 -55
  122. data/lib/phusion_passenger/platform_info/operating_system.rb +149 -149
  123. data/lib/phusion_passenger/platform_info/ruby.rb +468 -448
  124. data/lib/phusion_passenger/platform_info/zlib.rb +9 -9
  125. data/lib/phusion_passenger/plugin.rb +66 -66
  126. data/lib/phusion_passenger/preloader_shared_helpers.rb +126 -126
  127. data/lib/phusion_passenger/public_api.rb +191 -191
  128. data/lib/phusion_passenger/rack/out_of_band_gc.rb +93 -94
  129. data/lib/phusion_passenger/rack/thread_handler_extension.rb +231 -227
  130. data/lib/phusion_passenger/request_handler.rb +567 -577
  131. data/lib/phusion_passenger/request_handler/thread_handler.rb +379 -381
  132. data/lib/phusion_passenger/ruby_core_enhancements.rb +86 -86
  133. data/lib/phusion_passenger/ruby_core_io_enhancements.rb +74 -74
  134. data/lib/phusion_passenger/simple_benchmarking.rb +25 -25
  135. data/lib/phusion_passenger/standalone/app_finder.rb +153 -150
  136. data/lib/phusion_passenger/standalone/command.rb +44 -40
  137. data/lib/phusion_passenger/standalone/config_utils.rb +53 -53
  138. data/lib/phusion_passenger/standalone/control_utils.rb +38 -59
  139. data/lib/phusion_passenger/standalone/main.rb +73 -73
  140. data/lib/phusion_passenger/standalone/start_command.rb +697 -685
  141. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +193 -155
  142. data/lib/phusion_passenger/standalone/start_command/nginx_engine.rb +162 -133
  143. data/lib/phusion_passenger/standalone/status_command.rb +64 -64
  144. data/lib/phusion_passenger/standalone/stop_command.rb +72 -72
  145. data/lib/phusion_passenger/standalone/version_command.rb +9 -9
  146. data/lib/phusion_passenger/union_station/connection.rb +32 -32
  147. data/lib/phusion_passenger/union_station/core.rb +251 -251
  148. data/lib/phusion_passenger/union_station/transaction.rb +126 -126
  149. data/lib/phusion_passenger/utils.rb +199 -167
  150. data/lib/phusion_passenger/utils/ansi_colors.rb +128 -128
  151. data/lib/phusion_passenger/utils/download.rb +196 -196
  152. data/lib/phusion_passenger/utils/file_system_watcher.rb +158 -158
  153. data/lib/phusion_passenger/utils/hosts_file_parser.rb +101 -101
  154. data/lib/phusion_passenger/utils/lock.rb +31 -31
  155. data/lib/phusion_passenger/utils/native_support_utils.rb +31 -31
  156. data/lib/phusion_passenger/utils/progress_bar.rb +26 -26
  157. data/lib/phusion_passenger/utils/shellwords.rb +20 -20
  158. data/lib/phusion_passenger/utils/terminal_choice_menu.rb +206 -206
  159. data/lib/phusion_passenger/utils/unseekable_socket.rb +272 -272
  160. data/lib/phusion_passenger/vendor/crash_watch/app.rb +129 -0
  161. data/lib/phusion_passenger/vendor/crash_watch/gdb_controller.rb +341 -0
  162. data/lib/phusion_passenger/vendor/crash_watch/version.rb +24 -0
  163. data/lib/phusion_passenger/vendor/daemon_controller.rb +877 -0
  164. data/lib/phusion_passenger/vendor/daemon_controller/lock_file.rb +127 -0
  165. data/lib/phusion_passenger/vendor/daemon_controller/spawn.rb +26 -0
  166. data/lib/phusion_passenger/vendor/daemon_controller/version.rb +29 -0
  167. data/packaging/rpm/passenger_spec/passenger.spec.template +0 -1
  168. data/passenger.gemspec +0 -1
  169. data/resources/templates/config/nginx_engine_compiler/possible_solutions_for_download_and_extraction_problems.txt.erb +27 -0
  170. data/resources/templates/standalone/config.erb +19 -15
  171. data/test/integration_tests/apache2_tests.rb +566 -566
  172. data/test/integration_tests/downloaded_binaries_tests.rb +126 -125
  173. data/test/integration_tests/native_packaging_spec.rb +296 -296
  174. data/test/integration_tests/nginx_tests.rb +393 -393
  175. data/test/integration_tests/shared/example_webapp_tests.rb +282 -280
  176. data/test/integration_tests/source_packaging_test.rb +138 -138
  177. data/test/integration_tests/spec_helper.rb +5 -5
  178. data/test/integration_tests/standalone_tests.rb +367 -367
  179. data/test/ruby/debug_logging_spec.rb +133 -133
  180. data/test/ruby/message_channel_spec.rb +186 -186
  181. data/test/ruby/rack/loader_spec.rb +28 -28
  182. data/test/ruby/rack/preloader_spec.rb +34 -34
  183. data/test/ruby/rails3.0/loader_spec.rb +12 -12
  184. data/test/ruby/rails3.0/preloader_spec.rb +18 -18
  185. data/test/ruby/rails3.1/loader_spec.rb +12 -12
  186. data/test/ruby/rails3.1/preloader_spec.rb +18 -18
  187. data/test/ruby/rails3.2/loader_spec.rb +12 -12
  188. data/test/ruby/rails3.2/preloader_spec.rb +18 -18
  189. data/test/ruby/rails4.0/loader_spec.rb +12 -12
  190. data/test/ruby/rails4.0/preloader_spec.rb +18 -18
  191. data/test/ruby/rails4.1/loader_spec.rb +12 -12
  192. data/test/ruby/rails4.1/preloader_spec.rb +18 -18
  193. data/test/ruby/request_handler_spec.rb +730 -730
  194. data/test/ruby/shared/loader_sharedspec.rb +224 -224
  195. data/test/ruby/shared/rails/union_station_extensions_sharedspec.rb +327 -327
  196. data/test/ruby/shared/ruby_loader_sharedspec.rb +47 -47
  197. data/test/ruby/spec_helper.rb +65 -65
  198. data/test/ruby/standalone/runtime_installer_spec.rb +384 -384
  199. data/test/ruby/union_station_spec.rb +276 -276
  200. data/test/ruby/utils/file_system_watcher_spec.rb +220 -220
  201. data/test/ruby/utils/hosts_file_parser.rb +248 -248
  202. data/test/ruby/utils/tee_input_spec.rb +215 -215
  203. data/test/ruby/utils/unseekable_socket_spec.rb +57 -57
  204. data/test/ruby/utils_spec.rb +21 -21
  205. data/test/stub/rack/config.ru +87 -87
  206. data/test/stub/rack/library.rb +8 -8
  207. data/test/stub/rack/start.rb +30 -30
  208. data/test/support/apache2_controller.rb +191 -191
  209. data/test/support/nginx_controller.rb +90 -99
  210. data/test/support/placebo-preloader.rb +57 -57
  211. data/test/support/test_helper.rb +435 -435
  212. metadata +11 -21
  213. metadata.gz.asc +7 -7
  214. data/lib/phusion_passenger/standalone/command2.rb +0 -292
  215. data/lib/phusion_passenger/standalone/start2_command.rb +0 -799
  216. data/resources/templates/standalone/download_tool_missing.txt.erb +0 -18
  217. data/resources/templates/standalone/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -17
  218. data/resources/templates/standalone/run_installer_as_root.txt.erb +0 -8
@@ -29,437 +29,452 @@ PhusionPassenger.require_passenger_lib 'utils/shellwords'
29
29
 
30
30
  module PhusionPassenger
31
31
 
32
- # Provides shared functions for loader and preloader apps.
33
- module LoaderSharedHelpers
34
- extend self
35
-
36
- # To be called by the (pre)loader as soon as possible.
37
- def init(options)
38
- Thread.main[:name] = "Main thread"
39
- # We don't dump PATH info because at this point it's
40
- # unlikely to be changed.
41
- dump_ruby_environment
42
- check_rvm_using_wrapper_script(options)
43
- return sanitize_spawn_options(options)
44
- end
45
-
46
- def check_rvm_using_wrapper_script(options)
47
- ruby = options["ruby"]
48
- if ruby =~ %r(/\.?rvm/) && ruby =~ %r(/bin/ruby$)
49
- raise "You've set the `PassengerRuby` (Apache) or `passenger_ruby` (Nginx) option to '#{ruby}'. " +
50
- "However, because you are using RVM, this is not allowed: the option must point to " +
51
- "an RVM wrapper script, not a raw Ruby binary. This is because RVM is implemented " +
52
- "through various environment variables, which are set through the wrapper script.\n" +
53
- "\n" +
54
- "To find out the correct value for `PassengerRuby`/`passenger_ruby`, please read:\n\n" +
55
- " #{APACHE2_DOC_URL}#PassengerRuby\n\n" +
56
- " #{NGINX_DOC_URL}#PassengerRuby\n\n" +
57
- "Scroll to section 'RVM helper tool'.\n" +
58
- "\n-------------------------\n"
59
- end
60
- end
61
-
62
- # To be called whenever the (pre)loader is about to abort with an error.
63
- def about_to_abort(exception = nil)
64
- dump_all_information
65
- # https://code.google.com/p/phusion-passenger/issues/detail?id=1039
66
- puts
67
- end
68
-
69
- def to_boolean(value)
70
- return !(value.nil? || value == false || value == "false")
71
- end
72
-
73
- def sanitize_spawn_options(options)
74
- defaults = {
75
- "app_type" => "rack",
76
- "environment" => "production",
77
- "print_exceptions" => true
78
- }
79
- options = defaults.merge(options)
80
- options["app_group_name"] = options["app_root"] if !options["app_group_name"]
81
- options["print_exceptions"] = to_boolean(options["print_exceptions"])
82
- options["analytics"] = to_boolean(options["analytics"])
83
- options["show_version_in_header"] = to_boolean(options["show_version_in_header"])
84
- options["log_level"] = options["log_level"].to_i if options["log_level"]
85
- # TODO: smart spawning is not supported when using ruby-debug. We should raise an error
86
- # in this case.
87
- options["debugger"] = to_boolean(options["debugger"])
88
- options["spawn_method"] = "direct" if options["debugger"]
89
-
90
- return options
91
- end
92
-
93
- def dump_all_information
94
- dump_ruby_environment
95
- dump_envvars
96
- dump_system_metrics
97
- end
98
-
99
- def dump_ruby_environment
100
- if dir = ENV['PASSENGER_DEBUG_DIR']
101
- File.open("#{dir}/ruby_info", "w") do |f|
102
- f.puts "RUBY_VERSION = #{RUBY_VERSION}"
103
- f.puts "RUBY_PLATFORM = #{RUBY_PLATFORM}"
104
- f.puts "RUBY_ENGINE = #{defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'nil'}"
105
- end
106
- File.open("#{dir}/load_path", "wb") do |f|
107
- $LOAD_PATH.each do |path|
108
- f.puts path
109
- end
110
- end
111
- File.open("#{dir}/loaded_libs", "wb") do |f|
112
- $LOADED_FEATURES.each do |filename|
113
- f.puts filename
114
- end
115
- end
116
-
117
- # We write to these files last because the 'require' calls can fail.
118
- require 'rbconfig' if !defined?(RbConfig::CONFIG)
119
- File.open("#{dir}/rbconfig", "wb") do |f|
120
- RbConfig::CONFIG.each_pair do |key, value|
121
- f.puts "#{key} = #{value}"
122
- end
123
- end
124
- begin
125
- require 'rubygems' if !defined?(Gem)
126
- rescue LoadError
127
- end
128
- if defined?(Gem)
129
- File.open("#{dir}/ruby_info", "a") do |f|
130
- f.puts "RubyGems version = #{Gem::VERSION}"
131
- if Gem.respond_to?(:path)
132
- f.puts "RubyGems paths = #{Gem.path.inspect}"
133
- else
134
- f.puts "RubyGems paths = unknown; incompatible RubyGems API"
135
- end
136
- end
137
- File.open("#{dir}/activated_gems", "wb") do |f|
138
- if Gem.respond_to?(:loaded_specs)
139
- Gem.loaded_specs.each_pair do |name, spec|
140
- f.puts "#{name} => #{spec.version}"
141
- end
142
- else
143
- f.puts "Unable to query this information; incompatible RubyGems API."
144
- end
145
- end
146
- end
147
- end
148
- rescue SystemCallError
149
- # Don't care.
150
- end
151
-
152
- def dump_envvars
153
- if dir = ENV['PASSENGER_DEBUG_DIR']
154
- File.open("#{dir}/envvars", "wb") do |f|
155
- ENV.each_pair do |key, value|
156
- f.puts "#{key} = #{value}"
157
- end
158
- end
159
- end
160
- rescue SystemCallError
161
- # Don't care.
162
- end
163
-
164
- def dump_system_metrics
165
- if dir = ENV['PASSENGER_DEBUG_DIR']
166
- # When invoked through Passenger Standalone, we want passenger-config
167
- # to use the HelperAgent in the Passsenger Standalone buildout directory,
168
- # because the one in the source root may not exist.
169
- command = [
170
- "env",
171
- "PASSENGER_LOCATION_CONFIGURATION_FILE=#{PhusionPassenger.install_spec}",
172
- "#{PhusionPassenger.bin_dir}/passenger-config",
173
- "system-metrics"
174
- ]
175
- contents = `#{Shellwords.join(command)}`
176
- if $? && $?.exitstatus == 0
177
- File.open("#{dir}/system_metrics", "wb") do |f|
178
- f.write(contents)
179
- end
180
- end
181
- end
182
- rescue SystemCallError
183
- # Don't care.
184
- end
185
-
186
- # Prepare an application process using rules for the given spawn options.
187
- # This method is to be called before loading the application code.
188
- #
189
- # +startup_file+ is the application type's startup file, e.g.
190
- # "config/environment.rb" for Rails apps and "config.ru" for Rack apps.
191
- # +options+ are the spawn options that were given.
192
- #
193
- # This function may modify +options+. The modified options are to be
194
- # passed to the request handler.
195
- def before_loading_app_code_step1(startup_file, options)
196
- DebugLogging.log_level = options["log_level"] if options["log_level"]
197
-
198
- # Instantiate the Union Station core if requested. Can be nil.
199
- PhusionPassenger.require_passenger_lib 'union_station/core'
200
- options["union_station_core"] = UnionStation::Core.new_from_options(options)
201
- end
202
-
203
- def run_load_path_setup_code(options)
204
- # rack-preloader.rb depends on the 'rack' library, but the app
205
- # might want us to use a bundled version instead of a
206
- # gem/apt-get/yum/whatever-installed version. Therefore we must setup
207
- # the correct load paths before requiring 'rack'.
208
- #
209
- # The most popular tool for bundling dependencies is Bundler. Bundler
210
- # works as follows:
211
- # - If the bundle is locked then a file .bundle/environment.rb exists
212
- # which will setup the load paths.
213
- # - If the bundle is not locked then the load paths must be set up by
214
- # calling Bundler.setup.
215
- # - Rails 3's boot.rb automatically loads .bundle/environment.rb or
216
- # calls Bundler.setup if that's not available.
217
- # - Other Rack apps might not have a boot.rb but we still want to setup
218
- # Bundler.
219
- # - Some Rails 2 apps might have explicitly added Bundler support.
220
- # These apps call Bundler.setup in their preinitializer.rb.
221
- #
222
- # So the strategy is as follows:
223
-
224
- # Our strategy might be completely unsuitable for the app or the
225
- # developer is using something other than Bundler, so we let the user
226
- # manually specify a load path setup file.
227
- if options["load_path_setup_file"]
228
- require File.expand_path(options["load_path_setup_file"])
229
-
230
- # The app developer may also override our strategy with this magic file.
231
- elsif File.exist?('config/setup_load_paths.rb')
232
- require File.expand_path('config/setup_load_paths')
233
-
234
- # Older versions of Bundler use .bundle/environment.rb as the Bundler
235
- # environment lock file. This has been replaced by Gemfile.lock in later
236
- # versions, but we still support the older mechanism.
237
- # If the Bundler environment lock file exists then load that. If it
238
- # exists then there's a 99.9% chance that loading it is the correct
239
- # thing to do.
240
- elsif File.exist?('.bundle/environment.rb')
241
- running_bundler(options) do
242
- require File.expand_path('.bundle/environment')
243
- end
244
-
245
- # If the legacy Bundler environment file doesn't exist then there are two
246
- # possibilities:
247
- # 1. Bundler is not used, in which case we don't have to do anything.
248
- # 2. Bundler *is* used, but either the user is using a newer Bundler versions,
249
- # or the gems are not locked. In either case, we're supposed to call
250
- # Bundler.setup.
251
- #
252
- # The existence of Gemfile indicates whether (2) is true:
253
- elsif File.exist?('Gemfile')
254
- # In case of Rails 3, config/boot.rb already calls Bundler.setup.
255
- # However older versions of Rails may not so loading boot.rb might
256
- # not be the correct thing to do. To be on the safe side we
257
- # call Bundler.setup ourselves; calling Bundler.setup twice is
258
- # harmless. If this isn't the correct thing to do after all then
259
- # there's always the load_path_setup_file option and
260
- # setup_load_paths.rb.
261
- running_bundler(options) do
262
- require 'rubygems'
263
- require 'bundler/setup'
264
- end
265
- end
266
-
267
-
268
- # !!! NOTE !!!
269
- # If the app is using Bundler then any dependencies required past this
270
- # point must be specified in the Gemfile. Like ruby-debug if debugging is on...
271
- end
272
-
273
- def before_loading_app_code_step2(options)
274
- # Do nothing.
275
- end
276
-
277
- # This method is to be called after loading the application code but
278
- # before forking a worker process.
279
- def after_loading_app_code(options)
280
- # Post-install framework extensions. Possibly preceded by a call to
281
- # PhusionPassenger.install_framework_extensions!
282
- if defined?(::Rails) && !defined?(::Rails::VERSION)
283
- require 'rails/version'
284
- end
285
- end
286
-
287
- def create_socket_address(protocol, address)
288
- if protocol == 'unix'
289
- return "unix:#{address}"
290
- elsif protocol == 'tcp'
291
- return "tcp://#{address}"
292
- else
293
- raise ArgumentError, "Unknown protocol '#{protocol}'"
294
- end
295
- end
296
-
297
- def advertise_readiness
298
- # https://code.google.com/p/phusion-passenger/issues/detail?id=1039
299
- puts
300
-
301
- puts "!> Ready"
302
- end
303
-
304
- def advertise_sockets(output, request_handler)
305
- request_handler.server_sockets.each_pair do |name, options|
306
- concurrency = PhusionPassenger.advertised_concurrency_level || options[:concurrency]
307
- output.puts "!> socket: #{name};#{options[:address]};#{options[:protocol]};#{concurrency}"
308
- end
309
- end
310
-
311
- # To be called before the request handler main loop is entered, but after the app
312
- # startup file has been loaded. This function will fire off necessary events
313
- # and perform necessary preparation tasks.
314
- #
315
- # +forked+ indicates whether the current worker process is forked off from
316
- # an ApplicationSpawner that has preloaded the app code.
317
- # +options+ are the spawn options that were passed.
318
- def before_handling_requests(forked, options)
319
- if forked
320
- # Reseed pseudo-random number generator for security reasons.
321
- srand
322
- end
323
-
324
- if options["process_title"] && !options["process_title"].empty?
325
- $0 = options["process_title"] + ": " + options["app_group_name"]
326
- end
327
-
328
- if forked && options["union_station_core"]
329
- options["union_station_core"].clear_connection
330
- end
331
-
332
- # If we were forked from a preloader process then clear or
333
- # re-establish ActiveRecord database connections. This prevents
334
- # child processes from concurrently accessing the same
335
- # database connection handles.
336
- if forked && defined?(ActiveRecord::Base)
337
- if ActiveRecord::Base.respond_to?(:clear_all_connections!)
338
- ActiveRecord::Base.clear_all_connections!
339
- elsif ActiveRecord::Base.respond_to?(:clear_active_connections!)
340
- ActiveRecord::Base.clear_active_connections!
341
- elsif ActiveRecord::Base.respond_to?(:connected?) &&
342
- ActiveRecord::Base.connected?
343
- ActiveRecord::Base.establish_connection
344
- end
345
- end
346
-
347
- # Fire off events.
348
- PhusionPassenger.call_event(:starting_worker_process, forked)
349
- if options["pool_account_username"] && options["pool_account_password_base64"]
350
- password = options["pool_account_password_base64"].unpack('m').first
351
- PhusionPassenger.call_event(:credentials,
352
- options["pool_account_username"], password)
353
- else
354
- PhusionPassenger.call_event(:credentials, nil, nil)
355
- end
356
- end
357
-
358
- # To be called after the request handler main loop is exited. This function
359
- # will fire off necessary events perform necessary cleanup tasks.
360
- def after_handling_requests
361
- PhusionPassenger.call_event(:stopping_worker_process)
362
- end
363
-
364
- private
365
- def running_bundler(options)
366
- yield
367
- rescue Exception => e
368
- if (defined?(Bundler::GemNotFound) && e.is_a?(Bundler::GemNotFound)) ||
369
- (defined?(Bundler::GitError) && e.is_a?(Bundler::GitError))
370
- PhusionPassenger.require_passenger_lib 'platform_info/ruby'
371
- comment =
372
- "<p>It looks like Bundler could not find a gem. Maybe you didn't install all the " +
373
- "gems that this application needs. To install your gems, please run:</p>\n\n" +
374
- " <pre class=\"commands\">bundle install</pre>\n\n"
375
- ruby = options["ruby"]
376
- if ruby =~ %r(^/usr/local/rvm/)
377
- comment <<
378
- "<p>If that didn't work, then maybe the problem is that your gems are installed " +
379
- "to <code>#{h home_dir}/.rvm/gems</code>, while at the same time you set " +
380
- "<code>PassengerRuby</code> (Apache) or <code>passenger_ruby</code> (Nginx) to " +
381
- "<code>#{h ruby}</code>. Because of the latter, RVM does not load gems from the " +
382
- "home directory.</p>\n\n" +
383
- "<p>To make RVM load gems from the home directory, you need to set " +
384
- "<code>PassengerRuby</code>/<code>passenger_ruby</code> to an RVM wrapper script " +
385
- "inside the home directory:</p>\n\n" +
386
- "<ol>\n" +
387
- " <li>Login as #{h whoami}.</li>\n"
388
- if PlatformInfo.rvm_installation_mode == :multi
389
- comment <<
390
- " <li>Enable RVM mixed mode by running:\n" +
391
- " <pre class=\"commands\">rvm user gemsets</pre></li>\n"
392
- end
393
- comment <<
394
- " <li>Run this to find out what to set <code>PassengerRuby</code>/<code>passenger_ruby</code> to:\n" +
395
- " <pre class=\"commands\">#{PlatformInfo.ruby_command} \\\n" +
396
- "#{PhusionPassenger.bin_dir}/passenger-config --detect-ruby</pre></li>\n" +
397
- "</ol>\n\n" +
398
- "<p>If that didn't help either, then maybe your application is being run under a " +
399
- "different environment than it's supposed to. Please check the following:</p>\n\n"
400
- else
401
- comment <<
402
- "<p>If that didn't work, then the problem is probably caused by your " +
403
- "application being run under a different environment than it's supposed to. " +
404
- "Please check the following:</p>\n\n"
405
- end
406
- comment << "<ol>\n"
407
- comment <<
408
- " <li>Is this app supposed to be run as the <code>#{h whoami}</code> user?</li>\n" +
409
- " <li>Is this app being run on the correct Ruby interpreter? Below you will\n" +
410
- " see which Ruby interpreter Phusion Passenger attempted to use.</li>\n"
411
- if PlatformInfo.in_rvm?
412
- comment <<
413
- " <li>Please check whether the correct RVM gemset is being used.</li>\n" +
414
- " <li>Sometimes, RVM gemsets may be broken.\n" +
415
- " <a href=\"https://github.com/phusion/passenger/wiki/Resetting-RVM-gemsets\">Try resetting them.</a></li>\n"
416
- end
417
- comment << "</ol>\n"
418
- prepend_exception_html_comment(e, comment)
419
- end
420
- raise e
421
- end
422
-
423
- def prepend_exception_html_comment(e, comment)
424
- # Since Exception doesn't allow changing the message, we monkeypatch
425
- # the #message and #to_s methods.
426
- separator = "\n<p>-------- The exception is as follows: -------</p>\n"
427
- new_message = comment + separator + h(e.message)
428
- new_s = comment + separator + h(e.to_s)
429
- metaclass = class << e; self; end
430
- metaclass.send(:define_method, :message) do
431
- new_message
432
- end
433
- metaclass.send(:define_method, :to_s) do
434
- new_s
435
- end
436
- metaclass.send(:define_method, :html?) do
437
- true
438
- end
439
- end
440
-
441
- def h(text)
442
- require 'erb' if !defined?(ERB)
443
- return ERB::Util.h(text)
444
- end
445
-
446
- def whoami
447
- require 'etc' if !defined?(Etc)
448
- begin
449
- user = Etc.getpwuid(Process.uid)
450
- rescue ArgumentError
451
- user = nil
452
- end
453
- if user
454
- return user.name
455
- else
456
- return "##{Process.uid}"
457
- end
458
- end
459
-
460
- def home_dir
461
- return PhusionPassenger.home_dir
462
- end
463
- end
32
+ # Provides shared functions for loader and preloader apps.
33
+ module LoaderSharedHelpers
34
+ extend self
35
+
36
+ # To be called by the (pre)loader as soon as possible.
37
+ def init(options)
38
+ Thread.main[:name] = "Main thread"
39
+ # We don't dump PATH info because at this point it's
40
+ # unlikely to be changed.
41
+ dump_ruby_environment
42
+ check_rvm_using_wrapper_script(options)
43
+ return sanitize_spawn_options(options)
44
+ end
45
+
46
+ def check_rvm_using_wrapper_script(options)
47
+ ruby = options["ruby"]
48
+ if ruby =~ %r(/\.?rvm/) && ruby =~ %r(/bin/ruby$)
49
+ raise "You've set the `PassengerRuby` (Apache) or `passenger_ruby` (Nginx) option to '#{ruby}'. " +
50
+ "However, because you are using RVM, this is not allowed: the option must point to " +
51
+ "an RVM wrapper script, not a raw Ruby binary. This is because RVM is implemented " +
52
+ "through various environment variables, which are set through the wrapper script.\n" +
53
+ "\n" +
54
+ "To find out the correct value for `PassengerRuby`/`passenger_ruby`, please read:\n\n" +
55
+ " #{APACHE2_DOC_URL}#PassengerRuby\n\n" +
56
+ " #{NGINX_DOC_URL}#PassengerRuby\n\n" +
57
+ "Scroll to section 'RVM helper tool'.\n" +
58
+ "\n-------------------------\n"
59
+ end
60
+ end
61
+
62
+ # To be called whenever the (pre)loader is about to abort with an error.
63
+ def about_to_abort(options, exception = nil)
64
+ dump_all_information(options)
65
+ # https://code.google.com/p/phusion-passenger/issues/detail?id=1039
66
+ puts
67
+ end
68
+
69
+ def to_boolean(value)
70
+ return !(value.nil? || value == false || value == "false")
71
+ end
72
+
73
+ def sanitize_spawn_options(options)
74
+ defaults = {
75
+ "app_type" => "rack",
76
+ "environment" => "production",
77
+ "print_exceptions" => true
78
+ }
79
+ options = defaults.merge(options)
80
+ options["app_group_name"] = options["app_root"] if !options["app_group_name"]
81
+ options["print_exceptions"] = to_boolean(options["print_exceptions"])
82
+ options["analytics"] = to_boolean(options["analytics"])
83
+ options["show_version_in_header"] = to_boolean(options["show_version_in_header"])
84
+ options["log_level"] = options["log_level"].to_i if options["log_level"]
85
+ # TODO: smart spawning is not supported when using ruby-debug. We should raise an error
86
+ # in this case.
87
+ options["debugger"] = to_boolean(options["debugger"])
88
+ options["spawn_method"] = "direct" if options["debugger"]
89
+
90
+ return options
91
+ end
92
+
93
+ def dump_all_information(options)
94
+ dump_ruby_environment
95
+ dump_envvars
96
+ dump_system_metrics(options)
97
+ end
98
+
99
+ def dump_ruby_environment
100
+ if dir = ENV['PASSENGER_DEBUG_DIR']
101
+ File.open("#{dir}/ruby_info", "w") do |f|
102
+ f.puts "RUBY_VERSION = #{RUBY_VERSION}"
103
+ f.puts "RUBY_PLATFORM = #{RUBY_PLATFORM}"
104
+ f.puts "RUBY_ENGINE = #{defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'nil'}"
105
+ end
106
+ File.open("#{dir}/load_path", "wb") do |f|
107
+ $LOAD_PATH.each do |path|
108
+ f.puts path
109
+ end
110
+ end
111
+ File.open("#{dir}/loaded_libs", "wb") do |f|
112
+ $LOADED_FEATURES.each do |filename|
113
+ f.puts filename
114
+ end
115
+ end
116
+
117
+ # We write to these files last because the 'require' calls can fail.
118
+ require 'rbconfig' if !defined?(RbConfig::CONFIG)
119
+ File.open("#{dir}/rbconfig", "wb") do |f|
120
+ RbConfig::CONFIG.each_pair do |key, value|
121
+ f.puts "#{key} = #{value}"
122
+ end
123
+ end
124
+ begin
125
+ require 'rubygems' if !defined?(Gem)
126
+ rescue LoadError
127
+ end
128
+ if defined?(Gem)
129
+ File.open("#{dir}/ruby_info", "a") do |f|
130
+ f.puts "RubyGems version = #{Gem::VERSION}"
131
+ if Gem.respond_to?(:path)
132
+ f.puts "RubyGems paths = #{Gem.path.inspect}"
133
+ else
134
+ f.puts "RubyGems paths = unknown; incompatible RubyGems API"
135
+ end
136
+ end
137
+ File.open("#{dir}/activated_gems", "wb") do |f|
138
+ if Gem.respond_to?(:loaded_specs)
139
+ Gem.loaded_specs.each_pair do |name, spec|
140
+ f.puts "#{name} => #{spec.version}"
141
+ end
142
+ else
143
+ f.puts "Unable to query this information; incompatible RubyGems API."
144
+ end
145
+ end
146
+ end
147
+ end
148
+ rescue SystemCallError
149
+ # Don't care.
150
+ end
151
+
152
+ def dump_envvars
153
+ if dir = ENV['PASSENGER_DEBUG_DIR']
154
+ File.open("#{dir}/envvars", "wb") do |f|
155
+ ENV.each_pair do |key, value|
156
+ f.puts "#{key} = #{value}"
157
+ end
158
+ end
159
+ end
160
+ rescue SystemCallError
161
+ # Don't care.
162
+ end
163
+
164
+ def dump_system_metrics(options)
165
+ if dir = ENV['PASSENGER_DEBUG_DIR']
166
+ # When invoked through Passenger Standalone, we want passenger-config
167
+ # to use the HelperAgent in the Passsenger Standalone buildout directory,
168
+ # because the one in the source root may not exist.
169
+ passenger_config = "#{PhusionPassenger.bin_dir}/passenger-config"
170
+ if is_ruby_program?(passenger_config)
171
+ ruby = options["ruby"]
172
+ else
173
+ ruby = nil
174
+ end
175
+ command = [
176
+ "env",
177
+ "PASSENGER_LOCATION_CONFIGURATION_FILE=#{PhusionPassenger.install_spec}",
178
+ ruby,
179
+ passenger_config,
180
+ "system-metrics"
181
+ ].compact
182
+ contents = `#{Shellwords.join(command)}`
183
+ if $? && $?.exitstatus == 0
184
+ File.open("#{dir}/system_metrics", "wb") do |f|
185
+ f.write(contents)
186
+ end
187
+ end
188
+ end
189
+ rescue SystemCallError
190
+ # Don't care.
191
+ end
192
+
193
+ def is_ruby_program?(path)
194
+ File.open(path, "rb") do |f|
195
+ f.readline =~ /ruby/
196
+ end
197
+ rescue EOFError
198
+ false
199
+ end
200
+
201
+ # Prepare an application process using rules for the given spawn options.
202
+ # This method is to be called before loading the application code.
203
+ #
204
+ # +startup_file+ is the application type's startup file, e.g.
205
+ # "config/environment.rb" for Rails apps and "config.ru" for Rack apps.
206
+ # +options+ are the spawn options that were given.
207
+ #
208
+ # This function may modify +options+. The modified options are to be
209
+ # passed to the request handler.
210
+ def before_loading_app_code_step1(startup_file, options)
211
+ DebugLogging.log_level = options["log_level"] if options["log_level"]
212
+
213
+ # Instantiate the Union Station core if requested. Can be nil.
214
+ PhusionPassenger.require_passenger_lib 'union_station/core'
215
+ options["union_station_core"] = UnionStation::Core.new_from_options(options)
216
+ end
217
+
218
+ def run_load_path_setup_code(options)
219
+ # rack-preloader.rb depends on the 'rack' library, but the app
220
+ # might want us to use a bundled version instead of a
221
+ # gem/apt-get/yum/whatever-installed version. Therefore we must setup
222
+ # the correct load paths before requiring 'rack'.
223
+ #
224
+ # The most popular tool for bundling dependencies is Bundler. Bundler
225
+ # works as follows:
226
+ # - If the bundle is locked then a file .bundle/environment.rb exists
227
+ # which will setup the load paths.
228
+ # - If the bundle is not locked then the load paths must be set up by
229
+ # calling Bundler.setup.
230
+ # - Rails 3's boot.rb automatically loads .bundle/environment.rb or
231
+ # calls Bundler.setup if that's not available.
232
+ # - Other Rack apps might not have a boot.rb but we still want to setup
233
+ # Bundler.
234
+ # - Some Rails 2 apps might have explicitly added Bundler support.
235
+ # These apps call Bundler.setup in their preinitializer.rb.
236
+ #
237
+ # So the strategy is as follows:
238
+
239
+ # Our strategy might be completely unsuitable for the app or the
240
+ # developer is using something other than Bundler, so we let the user
241
+ # manually specify a load path setup file.
242
+ if options["load_path_setup_file"]
243
+ require File.expand_path(options["load_path_setup_file"])
244
+
245
+ # The app developer may also override our strategy with this magic file.
246
+ elsif File.exist?('config/setup_load_paths.rb')
247
+ require File.expand_path('config/setup_load_paths')
248
+
249
+ # Older versions of Bundler use .bundle/environment.rb as the Bundler
250
+ # environment lock file. This has been replaced by Gemfile.lock in later
251
+ # versions, but we still support the older mechanism.
252
+ # If the Bundler environment lock file exists then load that. If it
253
+ # exists then there's a 99.9% chance that loading it is the correct
254
+ # thing to do.
255
+ elsif File.exist?('.bundle/environment.rb')
256
+ running_bundler(options) do
257
+ require File.expand_path('.bundle/environment')
258
+ end
259
+
260
+ # If the legacy Bundler environment file doesn't exist then there are two
261
+ # possibilities:
262
+ # 1. Bundler is not used, in which case we don't have to do anything.
263
+ # 2. Bundler *is* used, but either the user is using a newer Bundler versions,
264
+ # or the gems are not locked. In either case, we're supposed to call
265
+ # Bundler.setup.
266
+ #
267
+ # The existence of Gemfile indicates whether (2) is true:
268
+ elsif File.exist?('Gemfile')
269
+ # In case of Rails 3, config/boot.rb already calls Bundler.setup.
270
+ # However older versions of Rails may not so loading boot.rb might
271
+ # not be the correct thing to do. To be on the safe side we
272
+ # call Bundler.setup ourselves; calling Bundler.setup twice is
273
+ # harmless. If this isn't the correct thing to do after all then
274
+ # there's always the load_path_setup_file option and
275
+ # setup_load_paths.rb.
276
+ running_bundler(options) do
277
+ require 'rubygems'
278
+ require 'bundler/setup'
279
+ end
280
+ end
281
+
282
+
283
+ # !!! NOTE !!!
284
+ # If the app is using Bundler then any dependencies required past this
285
+ # point must be specified in the Gemfile. Like ruby-debug if debugging is on...
286
+ end
287
+
288
+ def before_loading_app_code_step2(options)
289
+ # Do nothing.
290
+ end
291
+
292
+ # This method is to be called after loading the application code but
293
+ # before forking a worker process.
294
+ def after_loading_app_code(options)
295
+ # Post-install framework extensions. Possibly preceded by a call to
296
+ # PhusionPassenger.install_framework_extensions!
297
+ if defined?(::Rails) && !defined?(::Rails::VERSION)
298
+ require 'rails/version'
299
+ end
300
+ end
301
+
302
+ def create_socket_address(protocol, address)
303
+ if protocol == 'unix'
304
+ return "unix:#{address}"
305
+ elsif protocol == 'tcp'
306
+ return "tcp://#{address}"
307
+ else
308
+ raise ArgumentError, "Unknown protocol '#{protocol}'"
309
+ end
310
+ end
311
+
312
+ def advertise_readiness
313
+ # https://code.google.com/p/phusion-passenger/issues/detail?id=1039
314
+ puts
315
+
316
+ puts "!> Ready"
317
+ end
318
+
319
+ def advertise_sockets(output, request_handler)
320
+ request_handler.server_sockets.each_pair do |name, options|
321
+ concurrency = PhusionPassenger.advertised_concurrency_level || options[:concurrency]
322
+ output.puts "!> socket: #{name};#{options[:address]};#{options[:protocol]};#{concurrency}"
323
+ end
324
+ end
325
+
326
+ # To be called before the request handler main loop is entered, but after the app
327
+ # startup file has been loaded. This function will fire off necessary events
328
+ # and perform necessary preparation tasks.
329
+ #
330
+ # +forked+ indicates whether the current worker process is forked off from
331
+ # an ApplicationSpawner that has preloaded the app code.
332
+ # +options+ are the spawn options that were passed.
333
+ def before_handling_requests(forked, options)
334
+ if forked
335
+ # Reseed pseudo-random number generator for security reasons.
336
+ srand
337
+ end
338
+
339
+ if options["process_title"] && !options["process_title"].empty?
340
+ $0 = options["process_title"] + ": " + options["app_group_name"]
341
+ end
342
+
343
+ if forked && options["union_station_core"]
344
+ options["union_station_core"].clear_connection
345
+ end
346
+
347
+ # If we were forked from a preloader process then clear or
348
+ # re-establish ActiveRecord database connections. This prevents
349
+ # child processes from concurrently accessing the same
350
+ # database connection handles.
351
+ if forked && defined?(ActiveRecord::Base)
352
+ if ActiveRecord::Base.respond_to?(:clear_all_connections!)
353
+ ActiveRecord::Base.clear_all_connections!
354
+ elsif ActiveRecord::Base.respond_to?(:clear_active_connections!)
355
+ ActiveRecord::Base.clear_active_connections!
356
+ elsif ActiveRecord::Base.respond_to?(:connected?) &&
357
+ ActiveRecord::Base.connected?
358
+ ActiveRecord::Base.establish_connection
359
+ end
360
+ end
361
+
362
+ # Fire off events.
363
+ PhusionPassenger.call_event(:starting_worker_process, forked)
364
+ if options["pool_account_username"] && options["pool_account_password_base64"]
365
+ password = options["pool_account_password_base64"].unpack('m').first
366
+ PhusionPassenger.call_event(:credentials,
367
+ options["pool_account_username"], password)
368
+ else
369
+ PhusionPassenger.call_event(:credentials, nil, nil)
370
+ end
371
+ end
372
+
373
+ # To be called after the request handler main loop is exited. This function
374
+ # will fire off necessary events perform necessary cleanup tasks.
375
+ def after_handling_requests
376
+ PhusionPassenger.call_event(:stopping_worker_process)
377
+ end
378
+
379
+ private
380
+ def running_bundler(options)
381
+ yield
382
+ rescue Exception => e
383
+ if (defined?(Bundler::GemNotFound) && e.is_a?(Bundler::GemNotFound)) ||
384
+ (defined?(Bundler::GitError) && e.is_a?(Bundler::GitError))
385
+ PhusionPassenger.require_passenger_lib 'platform_info/ruby'
386
+ comment =
387
+ "<p>It looks like Bundler could not find a gem. Maybe you didn't install all the " +
388
+ "gems that this application needs. To install your gems, please run:</p>\n\n" +
389
+ " <pre class=\"commands\">bundle install</pre>\n\n"
390
+ ruby = options["ruby"]
391
+ if ruby =~ %r(^/usr/local/rvm/)
392
+ comment <<
393
+ "<p>If that didn't work, then maybe the problem is that your gems are installed " +
394
+ "to <code>#{h home_dir}/.rvm/gems</code>, while at the same time you set " +
395
+ "<code>PassengerRuby</code> (Apache) or <code>passenger_ruby</code> (Nginx) to " +
396
+ "<code>#{h ruby}</code>. Because of the latter, RVM does not load gems from the " +
397
+ "home directory.</p>\n\n" +
398
+ "<p>To make RVM load gems from the home directory, you need to set " +
399
+ "<code>PassengerRuby</code>/<code>passenger_ruby</code> to an RVM wrapper script " +
400
+ "inside the home directory:</p>\n\n" +
401
+ "<ol>\n" +
402
+ " <li>Login as #{h whoami}.</li>\n"
403
+ if PlatformInfo.rvm_installation_mode == :multi
404
+ comment <<
405
+ " <li>Enable RVM mixed mode by running:\n" +
406
+ " <pre class=\"commands\">rvm user gemsets</pre></li>\n"
407
+ end
408
+ comment <<
409
+ " <li>Run this to find out what to set <code>PassengerRuby</code>/<code>passenger_ruby</code> to:\n" +
410
+ " <pre class=\"commands\">#{PlatformInfo.ruby_command} \\\n" +
411
+ "#{PhusionPassenger.bin_dir}/passenger-config --detect-ruby</pre></li>\n" +
412
+ "</ol>\n\n" +
413
+ "<p>If that didn't help either, then maybe your application is being run under a " +
414
+ "different environment than it's supposed to. Please check the following:</p>\n\n"
415
+ else
416
+ comment <<
417
+ "<p>If that didn't work, then the problem is probably caused by your " +
418
+ "application being run under a different environment than it's supposed to. " +
419
+ "Please check the following:</p>\n\n"
420
+ end
421
+ comment << "<ol>\n"
422
+ comment <<
423
+ " <li>Is this app supposed to be run as the <code>#{h whoami}</code> user?</li>\n" +
424
+ " <li>Is this app being run on the correct Ruby interpreter? Below you will\n" +
425
+ " see which Ruby interpreter Phusion Passenger attempted to use.</li>\n"
426
+ if PlatformInfo.in_rvm?
427
+ comment <<
428
+ " <li>Please check whether the correct RVM gemset is being used.</li>\n" +
429
+ " <li>Sometimes, RVM gemsets may be broken.\n" +
430
+ " <a href=\"https://github.com/phusion/passenger/wiki/Resetting-RVM-gemsets\">Try resetting them.</a></li>\n"
431
+ end
432
+ comment << "</ol>\n"
433
+ prepend_exception_html_comment(e, comment)
434
+ end
435
+ raise e
436
+ end
437
+
438
+ def prepend_exception_html_comment(e, comment)
439
+ # Since Exception doesn't allow changing the message, we monkeypatch
440
+ # the #message and #to_s methods.
441
+ separator = "\n<p>-------- The exception is as follows: -------</p>\n"
442
+ new_message = comment + separator + h(e.message)
443
+ new_s = comment + separator + h(e.to_s)
444
+ metaclass = class << e; self; end
445
+ metaclass.send(:define_method, :message) do
446
+ new_message
447
+ end
448
+ metaclass.send(:define_method, :to_s) do
449
+ new_s
450
+ end
451
+ metaclass.send(:define_method, :html?) do
452
+ true
453
+ end
454
+ end
455
+
456
+ def h(text)
457
+ require 'erb' if !defined?(ERB)
458
+ return ERB::Util.h(text)
459
+ end
460
+
461
+ def whoami
462
+ require 'etc' if !defined?(Etc)
463
+ begin
464
+ user = Etc.getpwuid(Process.uid)
465
+ rescue ArgumentError
466
+ user = nil
467
+ end
468
+ if user
469
+ return user.name
470
+ else
471
+ return "##{Process.uid}"
472
+ end
473
+ end
474
+
475
+ def home_dir
476
+ return PhusionPassenger.home_dir
477
+ end
478
+ end
464
479
 
465
480
  end # module PhusionPassenger