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
@@ -1,5 +1,5 @@
1
1
  # Phusion Passenger - https://www.phusionpassenger.com/
2
- # Copyright (c) 2010-2014 Phusion
2
+ # Copyright (c) 2010-2015 Phusion
3
3
  #
4
4
  # "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
5
5
  #
@@ -27,6 +27,7 @@ PhusionPassenger.require_passenger_lib 'constants'
27
27
  PhusionPassenger.require_passenger_lib 'ruby_core_enhancements'
28
28
  PhusionPassenger.require_passenger_lib 'standalone/command'
29
29
  PhusionPassenger.require_passenger_lib 'standalone/config_utils'
30
+ PhusionPassenger.require_passenger_lib 'utils'
30
31
  PhusionPassenger.require_passenger_lib 'utils/tmpio'
31
32
 
32
33
  # We lazy load as many libraries as possible not only to improve startup performance,
@@ -34,688 +35,699 @@ PhusionPassenger.require_passenger_lib 'utils/tmpio'
34
35
  # checking stage of the runtime installer.
35
36
 
36
37
  module PhusionPassenger
37
- module Standalone
38
-
39
- class StartCommand < Command
40
- def self.create_default_options
41
- return {
42
- :environment => ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['NODE_ENV'] ||
43
- ENV['PASSENGER_APP_ENV'] || 'development',
44
- :spawn_method => Kernel.respond_to?(:fork) ? DEFAULT_SPAWN_METHOD : 'direct',
45
- :engine => "builtin",
46
- :nginx_version => PREFERRED_NGINX_VERSION,
47
- :log_level => DEFAULT_LOG_LEVEL,
48
- :ctls => []
49
- }
50
- end
51
-
52
- def run
53
- load_local_config_file
54
- parse_options
55
- sanity_check_options_and_set_defaults
56
-
57
- lookup_runtime_and_ensure_installed
58
- set_stdout_stderr_binmode
59
- exit if @options[:runtime_check_only]
60
-
61
- find_apps
62
- find_pid_and_log_file(@app_finder, @options)
63
- create_working_dir
64
- begin
65
- initialize_vars
66
- start_engine
67
- show_intro_message
68
- maybe_daemonize
69
- touch_temp_dir_in_background
70
- ########################
71
- ########################
72
- watch_log_files_in_background if should_watch_logs?
73
- wait_until_engine_has_exited if should_wait_until_engine_has_exited?
74
- rescue Interrupt
75
- shutdown_and_cleanup(true)
76
- exit 2
77
- rescue SignalException => signal
78
- shutdown_and_cleanup(true)
79
- if signal.message == 'SIGINT' || signal.message == 'SIGTERM'
80
- exit 2
81
- else
82
- raise
83
- end
84
- rescue Exception
85
- shutdown_and_cleanup(true)
86
- raise
87
- else
88
- shutdown_and_cleanup(false)
89
- end
90
- end
91
-
92
- private
93
- ################# Configuration loading, option parsing and initialization ###################
94
-
95
- def self.create_option_parser(options)
96
- # If you add or change an option, make sure to update the following places too:
97
- # lib/phusion_passenger/standalone/start_command/builtin_engine.rb, #build_daemon_controller_options
98
- # resources/templates/config/standalone.erb
99
- OptionParser.new do |opts|
100
- nl = "\n" + ' ' * 37
101
- opts.banner = "Usage: passenger start [DIRECTORY] [OPTIONS]\n"
102
- opts.separator "Starts #{PROGRAM_NAME} Standalone and serve one or more web applications."
103
- opts.separator ""
104
-
105
- opts.separator "Server options:"
106
- opts.on("-a", "--address HOST", String, "Bind to the given address.#{nl}" +
107
- "Default: 0.0.0.0") do |value|
108
- options[:address] = value
109
- end
110
- opts.on("-p", "--port NUMBER", Integer,
111
- "Use the given port number. Default: 3000") do |value|
112
- options[:port] = value
113
- end
114
- opts.on("-S", "--socket FILE", String,
115
- "Bind to Unix domain socket instead of TCP#{nl}" +
116
- "socket") do |value|
117
- options[:socket_file] = value
118
- end
119
- opts.on("--ssl", "Enable SSL support (Nginx#{nl}" +
120
- "engine only)") do
121
- options[:ssl] = true
122
- end
123
- opts.on("--ssl-certificate PATH", String,
124
- "Specify the SSL certificate path#{nl}" +
125
- "(Nginx engine only)") do |value|
126
- options[:ssl_certificate] = File.absolute_path_no_resolve(value)
127
- end
128
- opts.on("--ssl-certificate-key PATH", String,
129
- "Specify the SSL key path") do |value|
130
- options[:ssl_certificate_key] = File.absolute_path_no_resolve(value)
131
- end
132
- opts.on("--ssl-port PORT", Integer,
133
- "Listen for SSL on this port, while#{nl}" +
134
- "listening for HTTP on the normal port#{nl}" +
135
- "(Nginx engine only)") do |value|
136
- options[:ssl_port] = value
137
- end
138
- opts.on("-d", "--daemonize", "Daemonize into the background") do
139
- options[:daemonize] = true
140
- end
141
- opts.on("--user USERNAME", String, "User to run as. Ignored unless#{nl}" +
142
- "running as root") do |value|
143
- options[:user] = value
144
- end
145
- opts.on("--log-file FILENAME", String,
146
- "Where to write log messages. Default:#{nl}" +
147
- "console, or /dev/null when daemonized") do |value|
148
- options[:log_file] = value
149
- end
150
- opts.on("--pid-file FILENAME", String, "Where to store the PID file") do |value|
151
- options[:pid_file] = value
152
- end
153
- opts.on("--instance-registry-dir PATH", String,
154
- "Use the given instance registry directory") do |value|
155
- options[:instance_registry_dir] = value
156
- end
157
- opts.on("--data-buffer-dir PATH", String,
158
- "Use the given data buffer directory") do |value|
159
- options[:data_buffer_dir] = value
160
- end
161
-
162
- opts.separator ""
163
- opts.separator "Application loading options:"
164
- opts.on("-e", "--environment ENV", String,
165
- "Framework environment.#{nl}" +
166
- "Default: #{options[:environment]}") do |value|
167
- options[:environment] = value
168
- end
169
- opts.on("-R", "--rackup FILE", String,
170
- "Consider application a Ruby app, and use#{nl}" +
171
- "the given rackup file") do |value|
172
- options[:app_type] = "rack"
173
- options[:startup_file] = value
174
- end
175
- opts.on("--app-type NAME", String,
176
- "Force app to be detected as the given type") do |value|
177
- options[:app_type] = value
178
- end
179
- opts.on("--startup-file FILENAME", String,
180
- "Force given startup file to be used") do |value|
181
- options[:startup_file] = value
182
- end
183
- opts.on("--spawn-method NAME", String,
184
- "The spawn method to use. Default: #{options[:spawn_method]}") do |value|
185
- options[:spawn_method] = value
186
- end
187
- opts.on("--static-files-dir PATH", String,
188
- "Specify the static files dir (Nginx engine#{nl}" +
189
- "only)") do |val|
190
- options[:static_files_dir] = File.absolute_path_no_resolve(val)
191
- end
192
- opts.on("--restart-dir PATH", String, "Specify the restart dir") do |val|
193
- options[:restart_dir] = File.absolute_path_no_resolve(val)
194
- end
195
- opts.on("--friendly-error-pages", "Turn on friendly error pages") do
196
- options[:friendly_error_pages] = true
197
- end
198
- opts.on("--no-friendly-error-pages", "Turn off friendly error pages") do
199
- options[:friendly_error_pages] = false
200
- end
201
- opts.on("--load-shell-envvars",
202
- "Load shell startup files before loading#{nl}" +
203
- "application") do
204
- options[:load_shell_envvars] = true
205
- end
206
-
207
- opts.separator ""
208
- opts.separator "Process management options:"
209
- opts.on("--max-pool-size NUMBER", Integer,
210
- "Maximum number of application processes.#{nl}" +
211
- "Default: #{DEFAULT_MAX_POOL_SIZE}") do |value|
212
- options[:max_pool_size] = value
213
- end
214
- opts.on("--min-instances NUMBER", Integer,
215
- "Minimum number of processes per#{nl}" +
216
- "application. Default: 1") do |value|
217
- options[:min_instances] = value
218
- end
219
- opts.on("--concurrency-model NAME", String,
220
- "The concurrency model to use, either#{nl}" +
221
- "'process' or 'thread' (Enterprise only).#{nl}" +
222
- "Default: #{DEFAULT_CONCURRENCY_MODEL}") do |value|
223
- options[:concurrency_model] = value
224
- end
225
- opts.on("--thread-count NAME", Integer,
226
- "The number of threads to use when using#{nl}" +
227
- "the 'thread' concurrency model (Enterprise#{nl}" +
228
- "only). Default: #{DEFAULT_APP_THREAD_COUNT}") do |value|
229
- options[:thread_count] = value
230
- end
231
- opts.on("--rolling-restarts", "Enable rolling restarts (Enterprise only)") do
232
- options[:rolling_restarts] = true
233
- end
234
- opts.on("--resist-deployment-errors", "Enable deployment error resistance#{nl}" +
235
- "(Enterprise only)") do
236
- options[:resist_deployment_errors] = true
237
- end
238
-
239
- opts.separator ""
240
- opts.separator "Request handling options:"
241
- opts.on("--sticky-sessions", "Enable sticky sessions") do
242
- options[:sticky_sessions] = true
243
- end
244
- opts.on("--sticky-sessions-cookie-name NAME", String,
245
- "Cookie name to use for sticky sessions.#{nl}" +
246
- "Default: #{DEFAULT_STICKY_SESSIONS_COOKIE_NAME}") do |value|
247
- options[:sticky_sessions_cookie_name] = value
248
- end
249
- opts.on("--vary-turbocache-by-cookie NAME", String,
250
- "Vary the turbocache by the cookie of the given name") do |value|
251
- options[:vary_turbocache_by_cookie] = value
252
- end
253
- opts.on("--disable-turbocaching", "Disable turbocaching") do
254
- options[:turbocaching] = false
255
- end
256
-
257
- opts.separator ""
258
- opts.separator "Union Station options:"
259
- opts.on("--union-station-gateway HOST:PORT", String,
260
- "Specify Union Station Gateway host and port") do |value|
261
- host, port = value.split(":", 2)
262
- port = port.to_i
263
- port = 443 if port == 0
264
- options[:union_station_gateway_address] = host
265
- options[:union_station_gateway_port] = port.to_i
266
- end
267
- opts.on("--union-station-key KEY", String, "Specify Union Station key") do |value|
268
- options[:union_station_key] = value
269
- end
270
-
271
- opts.separator ""
272
- opts.separator "Nginx engine options:"
273
- opts.on("--nginx-bin FILENAME", String, "Nginx binary to use as core") do |value|
274
- options[:nginx_bin] = value
275
- end
276
- opts.on("--nginx-version VERSION", String,
277
- "Nginx version to use as core.#{nl}" +
278
- "Default: #{PREFERRED_NGINX_VERSION}") do |value|
279
- options[:nginx_version] = value
280
- end
281
- opts.on("--nginx-tarball FILENAME", String,
282
- "If Nginx needs to be installed, then the#{nl}" +
283
- "given tarball will be used instead of#{nl}" +
284
- "downloading from the Internet") do |value|
285
- options[:nginx_tarball] = File.absolute_path_no_resolve(value)
286
- end
287
- opts.on("--nginx-config-template FILENAME", String,
288
- "The template to use for generating the#{nl}" +
289
- "Nginx config file") do |value|
290
- options[:nginx_config_template] = File.absolute_path_no_resolve(value)
291
- end
292
-
293
- opts.separator ""
294
- opts.separator "Advanced options:"
295
- opts.on("--engine NAME", String,
296
- "Underlying HTTP engine to use. Available#{nl}" +
297
- "options: builtin (default), nginx") do |value|
298
- options[:engine] = value
299
- end
300
- opts.on("--log-level NUMBER", Integer, "Log level to use. Default: #{DEFAULT_LOG_LEVEL}") do |value|
301
- options[:log_level] = value
302
- end
303
- opts.on("--ctl NAME=VALUE", String) do |value|
304
- if value !~ /=.+/
305
- abort "*** ERROR: invalid --ctl format: #{value}"
306
- end
307
- options[:ctls] << value
308
- end
309
- opts.on("--binaries-url-root URL", String,
310
- "If Nginx needs to be installed, then the#{nl}" +
311
- "specified URL will be checked for binaries#{nl}" +
312
- "prior to a local build") do |value|
313
- options[:binaries_url_root] = value
314
- end
315
- opts.on("--no-download-binaries", "Never download binaries") do
316
- options[:download_binaries] = false
317
- end
318
- opts.on("--runtime-check-only",
319
- "Quit after checking whether the#{nl}" +
320
- "#{PROGRAM_NAME} Standalone runtime files#{nl}" +
321
- "are installed") do
322
- options[:runtime_check_only] = true
323
- end
324
- opts.on("--no-install-runtime", "Abort if runtime must be installed") do
325
- options[:dont_install_runtime] = true
326
- end
327
- opts.on("--no-compile-runtime", "Abort if runtime must be compiled") do
328
- options[:dont_compile_runtime] = true
329
- end
330
- end
331
- end
332
-
333
- def load_local_config_file
334
- if @argv.empty?
335
- app_dir = File.absolute_path_no_resolve(".")
336
- elsif @argv.size == 1
337
- app_dir = @argv[0]
338
- end
339
- if app_dir
340
- begin
341
- ConfigUtils.load_local_config_file!(app_dir, @options)
342
- rescue ConfigLoadError => e
343
- abort "*** ERROR: #{e.message}"
344
- end
345
- end
346
- end
347
-
348
- def sanity_check_options_and_set_defaults
349
- if (@options[:address] || @options[:port]) && @options[:socket_file]
350
- abort "You cannot specify both --address/--port and --socket. Please choose either one."
351
- end
352
- if @options[:ssl] && !@options[:ssl_certificate]
353
- abort "You specified --ssl. Please specify --ssl-certificate as well."
354
- end
355
- if @options[:ssl] && !@options[:ssl_certificate_key]
356
- abort "You specified --ssl. Please specify --ssl-certificate-key as well."
357
- end
358
- if @options[:engine] != "builtin" && @options[:engine] != "nginx"
359
- abort "You've specified an invalid value for --engine. The only values allowed are: builtin, nginx."
360
- end
361
-
362
- if !@options[:socket_file]
363
- @options[:address] ||= "0.0.0.0"
364
- @options[:port] ||= 3000
365
- end
366
-
367
- if @options[:engine] == "builtin"
368
- # We explicitly check for some options are set and warn the user about this,
369
- # in case they forget to pass --engine=nginx. We don't warn about options
370
- # that begin with --nginx- because that should be obvious.
371
- check_nginx_option_used_with_builtin_engine(:ssl, "--ssl")
372
- check_nginx_option_used_with_builtin_engine(:ssl_certificate, "--ssl-certificate")
373
- check_nginx_option_used_with_builtin_engine(:ssl_certificate_key, "--ssl-certificate-key")
374
- check_nginx_option_used_with_builtin_engine(:ssl_port, "--ssl-port")
375
- check_nginx_option_used_with_builtin_engine(:static_files_dir, "--static-files-dir")
376
- end
377
-
378
- #############
379
- end
380
-
381
- def check_nginx_option_used_with_builtin_engine(option, argument)
382
- if @options.has_key?(option)
383
- STDERR.puts "*** Warning: the #{argument} option is only allowed if you use " +
384
- "the 'nginx' engine. You are currently using the 'builtin' engine, so " +
385
- "this option has been ignored. To switch to the Nginx engine, please " +
386
- "pass --engine=nginx."
387
- end
388
- end
389
-
390
- def lookup_runtime_and_ensure_installed
391
- @agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE)
392
- if @options[:nginx_bin]
393
- @nginx_binary = @options[:nginx_bin]
394
- if !@nginx_binary
395
- abort "*** ERROR: Nginx binary #{@options[:nginx_bin]} does not exist"
396
- end
397
- if !@agent_exe
398
- install_runtime
399
- @agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE)
400
- end
401
- else
402
- nginx_name = "nginx-#{@options[:nginx_version]}"
403
- @nginx_binary = PhusionPassenger.find_support_binary(nginx_name)
404
- if !@agent_exe || !@nginx_binary
405
- install_runtime
406
- @agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE)
407
- @nginx_binary = PhusionPassenger.find_support_binary(nginx_name)
408
- end
409
- end
410
- end
411
-
412
- def install_runtime
413
- if @options[:dont_install_runtime]
414
- abort "*** ERROR: Refusing to install the #{PROGRAM_NAME} Standalone runtime " +
415
- "because --no-install-runtime is given."
416
- end
417
-
418
- args = ["--brief", "--no-force-tip"]
419
- if @options[:binaries_url_root]
420
- args << "--url-root"
421
- args << @options[:binaries_url_root]
422
- end
423
- if @options[:nginx_version]
424
- args << "--nginx-version"
425
- args << @options[:nginx_version]
426
- end
427
- if @options[:nginx_tarball]
428
- args << "--nginx-tarball"
429
- args << @options[:nginx_tarball]
430
- end
431
- if @options[:dont_compile_runtime]
432
- args << "--no-compile"
433
- end
434
- PhusionPassenger.require_passenger_lib 'config/install_standalone_runtime_command'
435
- PhusionPassenger::Config::InstallStandaloneRuntimeCommand.new(args).run
436
- puts
437
- puts "--------------------------"
438
- puts
439
- end
440
-
441
- def set_stdout_stderr_binmode
442
- # We already set STDOUT and STDERR to binmode in bin/passenger, which
443
- # fixes https://github.com/phusion/passenger-ruby-heroku-demo/issues/11.
444
- # However RuntimeInstaller sets them to UTF-8, so here we set them back.
445
- STDOUT.binmode
446
- STDERR.binmode
447
- end
448
-
449
-
450
- ################## Core logic ##################
451
-
452
- def find_apps
453
- PhusionPassenger.require_passenger_lib 'standalone/app_finder'
454
- @app_finder = AppFinder.new(@argv, @options)
455
- @apps = @app_finder.scan
456
- if @app_finder.multi_mode? && @options[:engine] != 'nginx'
457
- puts "Mass deployment enabled, so forcing engine to 'nginx'."
458
- @options[:engine] = 'nginx'
459
- end
460
- end
461
-
462
- def find_pid_and_log_file(app_finder, options)
463
- exec_root = app_finder.execution_root
464
- if options[:socket_file]
465
- pid_basename = "passenger.pid"
466
- log_basename = "passenger.log"
467
- else
468
- pid_basename = "passenger.#{options[:port]}.pid"
469
- log_basename = "passenger.#{options[:port]}.log"
470
- end
471
- if File.directory?("#{exec_root}/tmp/pids")
472
- options[:pid_file] ||= "#{exec_root}/tmp/pids/#{pid_basename}"
473
- else
474
- options[:pid_file] ||= "#{exec_root}/#{pid_basename}"
475
- end
476
- if File.directory?("log")
477
- options[:log_file] ||= "#{exec_root}/log/#{log_basename}"
478
- else
479
- options[:log_file] ||= "#{exec_root}/#{log_basename}"
480
- end
481
- end
482
-
483
- def create_working_dir
484
- # We don't remove the working dir in 'passenger start'. In daemon
485
- # mode 'passenger start' just quits and lets background processes
486
- # running. A specific background process, temp-dir-toucher, is
487
- # responsible for cleaning up the working dir.
488
- @working_dir = PhusionPassenger::Utils.mktmpdir("passenger-standalone.")
489
- File.chmod(0755, @working_dir)
490
- Dir.mkdir("#{@working_dir}/logs")
491
- @can_remove_working_dir = true
492
- end
493
-
494
- def initialize_vars
495
- @console_mutex = Mutex.new
496
- @termination_pipe = IO.pipe
497
- @threads = []
498
- @interruptable_threads = []
499
- end
500
-
501
- def start_engine
502
- metaclass = class << self; self; end
503
- if @options[:engine] == 'nginx'
504
- PhusionPassenger.require_passenger_lib 'standalone/start_command/nginx_engine'
505
- metaclass.send(:include, PhusionPassenger::Standalone::StartCommand::NginxEngine)
506
- else
507
- PhusionPassenger.require_passenger_lib 'standalone/start_command/builtin_engine'
508
- metaclass.send(:include, PhusionPassenger::Standalone::StartCommand::BuiltinEngine)
509
- end
510
- start_engine_real
511
- end
512
-
513
- # Returns the URL that the server will be listening on.
514
- def listen_url
515
- if @options[:socket_file]
516
- return @options[:socket_file]
517
- else
518
- if @options[:ssl] && !@options[:ssl_port]
519
- scheme = "https"
520
- else
521
- scheme = "http"
522
- end
523
- result = "#{scheme}://"
524
- if @options[:port] == 80
525
- result << @options[:address]
526
- else
527
- result << compose_ip_and_port(@options[:address], @options[:port])
528
- end
529
- result << "/"
530
- return result
531
- end
532
- end
533
-
534
- def compose_ip_and_port(ip, port)
535
- if ip =~ /:/
536
- # IPv6
537
- return "[#{ip}]:#{port}"
538
- else
539
- return "#{ip}:#{port}"
540
- end
541
- end
542
-
543
- def show_intro_message
544
- puts "=============== Phusion Passenger Standalone web server started ==============="
545
- puts "PID file: #{@options[:pid_file]}"
546
- puts "Log file: #{@options[:log_file]}"
547
- puts "Environment: #{@options[:environment]}"
548
- puts "Accessible via: #{listen_url}"
549
-
550
- puts
551
- if @options[:daemonize]
552
- puts "Serving in the background as a daemon."
553
- else
554
- puts "You can stop #{PROGRAM_NAME} Standalone by pressing Ctrl-C."
555
- end
556
- puts "Problems? Check #{STANDALONE_DOC_URL}#troubleshooting"
557
- puts "==============================================================================="
558
- end
559
-
560
- def maybe_daemonize
561
- if @options[:daemonize]
562
- PhusionPassenger.require_passenger_lib 'platform_info/ruby'
563
- if PlatformInfo.ruby_supports_fork?
564
- daemonize
565
- else
566
- abort "Unable to daemonize using the current Ruby interpreter " +
567
- "(#{PlatformInfo.ruby_command}) because it does not support forking."
568
- end
569
- end
570
- end
571
-
572
- def daemonize
573
- pid = fork
574
- if pid
575
- # Parent
576
- exit!(0)
577
- else
578
- # Child
579
- trap "HUP", "IGNORE"
580
- STDIN.reopen("/dev/null", "r")
581
- STDOUT.reopen(@options[:log_file], "a")
582
- STDERR.reopen(@options[:log_file], "a")
583
- STDOUT.sync = true
584
- STDERR.sync = true
585
- Process.setsid
586
- @threads = nil
587
- end
588
- end
589
-
590
- def touch_temp_dir_in_background
591
- result = system(@agent_exe,
592
- "temp-dir-toucher",
593
- @working_dir,
594
- "--cleanup",
595
- "--daemonize",
596
- "--pid-file", "#{@working_dir}/temp_dir_toucher.pid",
597
- "--log-file", @options[:log_file])
598
- if !result
599
- abort "Cannot start #{@agent_exe} temp-dir-toucher"
600
- end
601
- @can_remove_working_dir = false
602
- end
603
-
604
- def watch_log_file(log_file)
605
- if File.exist?(log_file)
606
- backward = 0
607
- else
608
- # tail bails out if the file doesn't exist, so wait until it exists.
609
- while !File.exist?(log_file)
610
- sleep 1
611
- end
612
- backward = 10
613
- end
614
-
615
- IO.popen("tail -f -n #{backward} \"#{log_file}\"", "rb") do |f|
616
- begin
617
- while true
618
- begin
619
- line = f.readline
620
- @console_mutex.synchronize do
621
- STDOUT.write(line)
622
- STDOUT.flush
623
- end
624
- rescue EOFError
625
- break
626
- end
627
- end
628
- ensure
629
- Process.kill('TERM', f.pid) rescue nil
630
- end
631
- end
632
- end
633
-
634
- def watch_log_files_in_background
635
- @apps.each do |app|
636
- thread = Thread.new do
637
- Thread.current.abort_on_exception = true
638
- watch_log_file("#{app[:root]}/log/#{@options[:environment]}.log")
639
- end
640
- @threads << thread
641
- @interruptable_threads << thread
642
- end
643
- thread = Thread.new do
644
- Thread.current.abort_on_exception = true
645
- watch_log_file(@options[:log_file])
646
- end
647
- @threads << thread
648
- @interruptable_threads << thread
649
- end
650
-
651
- def should_watch_logs?
652
- return !@options[:daemonize] && @options[:log_file] != "/dev/null"
653
- end
654
-
655
- def wait_until_engine_has_exited
656
- # Since the engine is not our child process (it daemonizes)
657
- # we cannot use Process.waitpid to wait for it. A busy-sleep-loop with
658
- # Process.kill(0, pid) isn't very efficient. Instead we do this:
659
- #
660
- # Connect to the engine's server and wait until it disconnects the socket
661
- # because of timeout. Keep doing this until we can no longer connect.
662
- while true
663
- if @options[:socket_file]
664
- socket = UNIXSocket.new(@options[:socket_file])
665
- else
666
- socket = TCPSocket.new(@options[:address], @options[:port])
667
- end
668
- begin
669
- socket.read rescue nil
670
- ensure
671
- socket.close rescue nil
672
- end
673
- end
674
- rescue Errno::ECONNREFUSED, Errno::ECONNRESET
675
- end
676
-
677
- def should_wait_until_engine_has_exited?
678
- return !@options[:daemonize] || @app_finder.multi_mode?
679
- end
680
-
681
-
682
- ################## Shut down and cleanup ##################
683
-
684
- def shutdown_and_cleanup(error_occurred)
685
- # Stop engine
686
- if @engine && (error_occurred || should_wait_until_engine_has_exited?)
687
- @console_mutex.synchronize do
688
- STDOUT.write("Stopping web server...")
689
- STDOUT.flush
690
- @engine.stop
691
- STDOUT.puts " done"
692
- STDOUT.flush
693
- end
694
- @engine = nil
695
- end
696
-
697
- # Stop threads
698
- if @threads
699
- if !@termination_pipe[1].closed?
700
- @termination_pipe[1].write("x")
701
- @termination_pipe[1].close
702
- end
703
- @interruptable_threads.each do |thread|
704
- thread.terminate
705
- end
706
- @interruptable_threads = []
707
- @threads.each do |thread|
708
- thread.join
709
- end
710
- @threads = nil
711
- end
712
-
713
- if @can_remove_working_dir
714
- FileUtils.remove_entry_secure(@working_dir)
715
- @can_remove_working_dir = false
716
- end
717
- end
718
- end
719
-
720
- end # module Standalone
38
+ module Standalone
39
+
40
+ class StartCommand < Command
41
+ DEFAULT_OPTIONS = {
42
+ :environment => ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['NODE_ENV'] ||
43
+ ENV['PASSENGER_APP_ENV'] || 'development',
44
+ :spawn_method => Kernel.respond_to?(:fork) ? DEFAULT_SPAWN_METHOD : 'direct',
45
+ :engine => "nginx",
46
+ :nginx_version => PREFERRED_NGINX_VERSION,
47
+ :log_level => DEFAULT_LOG_LEVEL,
48
+ :ctls => [],
49
+ :envvars => {}
50
+ }.freeze
51
+
52
+ def run
53
+ parse_options
54
+ load_local_config_file
55
+ remerge_all_options
56
+ sanity_check_options_and_set_defaults
57
+
58
+ lookup_runtime_and_ensure_installed
59
+ set_stdout_stderr_binmode
60
+ exit if @options[:runtime_check_only]
61
+
62
+ find_apps
63
+ find_pid_and_log_file(@app_finder, @options)
64
+ create_working_dir
65
+ begin
66
+ initialize_vars
67
+ start_engine
68
+ show_intro_message
69
+ maybe_daemonize
70
+ touch_temp_dir_in_background
71
+ ########################
72
+ ########################
73
+ watch_log_files_in_background if should_watch_logs?
74
+ wait_until_engine_has_exited if should_wait_until_engine_has_exited?
75
+ rescue Interrupt
76
+ shutdown_and_cleanup(true)
77
+ exit 2
78
+ rescue SignalException => signal
79
+ shutdown_and_cleanup(true)
80
+ if signal.message == 'SIGINT' || signal.message == 'SIGTERM'
81
+ exit 2
82
+ else
83
+ raise
84
+ end
85
+ rescue Exception
86
+ shutdown_and_cleanup(true)
87
+ raise
88
+ else
89
+ shutdown_and_cleanup(false)
90
+ end
91
+ end
92
+
93
+ private
94
+ ################# Configuration loading, option parsing and initialization ###################
95
+
96
+ def self.create_option_parser(options)
97
+ # If you add or change an option, make sure to update the following places too:
98
+ # lib/phusion_passenger/standalone/start_command/builtin_engine.rb, #build_daemon_controller_options
99
+ # resources/templates/config/standalone.erb
100
+ OptionParser.new do |opts|
101
+ nl = "\n" + ' ' * 37
102
+ opts.banner = "Usage: passenger start [DIRECTORY] [OPTIONS]\n"
103
+ opts.separator "Starts #{PROGRAM_NAME} Standalone and serve one or more web applications."
104
+ opts.separator ""
105
+
106
+ opts.separator "Server options:"
107
+ opts.on("-a", "--address HOST", String, "Bind to the given address.#{nl}" +
108
+ "Default: 0.0.0.0") do |value|
109
+ options[:address] = value
110
+ end
111
+ opts.on("-p", "--port NUMBER", Integer,
112
+ "Use the given port number. Default: 3000") do |value|
113
+ options[:port] = value
114
+ end
115
+ opts.on("-S", "--socket FILE", String,
116
+ "Bind to Unix domain socket instead of TCP#{nl}" +
117
+ "socket") do |value|
118
+ options[:socket_file] = value
119
+ end
120
+ opts.on("--ssl", "Enable SSL support (Nginx#{nl}" +
121
+ "engine only)") do
122
+ options[:ssl] = true
123
+ end
124
+ opts.on("--ssl-certificate PATH", String,
125
+ "Specify the SSL certificate path#{nl}" +
126
+ "(Nginx engine only)") do |value|
127
+ options[:ssl_certificate] = File.absolute_path_no_resolve(value)
128
+ end
129
+ opts.on("--ssl-certificate-key PATH", String,
130
+ "Specify the SSL key path") do |value|
131
+ options[:ssl_certificate_key] = File.absolute_path_no_resolve(value)
132
+ end
133
+ opts.on("--ssl-port PORT", Integer,
134
+ "Listen for SSL on this port, while#{nl}" +
135
+ "listening for HTTP on the normal port#{nl}" +
136
+ "(Nginx engine only)") do |value|
137
+ options[:ssl_port] = value
138
+ end
139
+ opts.on("-d", "--daemonize", "Daemonize into the background") do
140
+ options[:daemonize] = true
141
+ end
142
+ opts.on("--user USERNAME", String, "User to run as. Ignored unless#{nl}" +
143
+ "running as root") do |value|
144
+ options[:user] = value
145
+ end
146
+ opts.on("--log-file FILENAME", String,
147
+ "Where to write log messages. Default:#{nl}" +
148
+ "console, or /dev/null when daemonized") do |value|
149
+ options[:log_file] = value
150
+ end
151
+ opts.on("--pid-file FILENAME", String, "Where to store the PID file") do |value|
152
+ options[:pid_file] = value
153
+ end
154
+ opts.on("--instance-registry-dir PATH", String,
155
+ "Use the given instance registry directory") do |value|
156
+ options[:instance_registry_dir] = value
157
+ end
158
+ opts.on("--data-buffer-dir PATH", String,
159
+ "Use the given data buffer directory") do |value|
160
+ options[:data_buffer_dir] = value
161
+ end
162
+
163
+ opts.separator ""
164
+ opts.separator "Application loading options:"
165
+ opts.on("-e", "--environment ENV", String,
166
+ "Framework environment.#{nl}" +
167
+ "Default: #{DEFAULT_OPTIONS[:environment]}") do |value|
168
+ options[:environment] = value
169
+ end
170
+ opts.on("-R", "--rackup FILE", String,
171
+ "Consider application a Ruby app, and use#{nl}" +
172
+ "the given rackup file") do |value|
173
+ options[:app_type] = "rack"
174
+ options[:startup_file] = value
175
+ end
176
+ opts.on("--app-type NAME", String,
177
+ "Force app to be detected as the given type") do |value|
178
+ options[:app_type] = value
179
+ end
180
+ opts.on("--startup-file FILENAME", String,
181
+ "Force given startup file to be used") do |value|
182
+ options[:startup_file] = value
183
+ end
184
+ opts.on("--spawn-method NAME", String,
185
+ "The spawn method to use. Default: #{DEFAULT_OPTIONS[:spawn_method]}") do |value|
186
+ options[:spawn_method] = value
187
+ end
188
+ opts.on("--static-files-dir PATH", String,
189
+ "Specify the static files dir (Nginx engine#{nl}" +
190
+ "only)") do |val|
191
+ options[:static_files_dir] = File.absolute_path_no_resolve(val)
192
+ end
193
+ opts.on("--restart-dir PATH", String, "Specify the restart dir") do |val|
194
+ options[:restart_dir] = File.absolute_path_no_resolve(val)
195
+ end
196
+ opts.on("--friendly-error-pages", "Turn on friendly error pages") do
197
+ options[:friendly_error_pages] = true
198
+ end
199
+ opts.on("--no-friendly-error-pages", "Turn off friendly error pages") do
200
+ options[:friendly_error_pages] = false
201
+ end
202
+ opts.on("--load-shell-envvars",
203
+ "Load shell startup files before loading#{nl}" +
204
+ "application") do
205
+ options[:load_shell_envvars] = true
206
+ end
207
+ opts.on("--debugger", "Enable debugger support") do
208
+ options[:debugger] = true
209
+ end
210
+
211
+ opts.separator ""
212
+ opts.separator "Process management options:"
213
+ opts.on("--max-pool-size NUMBER", Integer,
214
+ "Maximum number of application processes.#{nl}" +
215
+ "Default: #{DEFAULT_MAX_POOL_SIZE}") do |value|
216
+ if value < 1
217
+ abort "*** ERROR: you may only specify for --max-pool-size a number greater than or equal to 1"
218
+ end
219
+ options[:max_pool_size] = value
220
+ end
221
+ opts.on("--min-instances NUMBER", Integer,
222
+ "Minimum number of processes per#{nl}" +
223
+ "application. Default: 1") do |value|
224
+ options[:min_instances] = value
225
+ end
226
+ opts.on("--concurrency-model NAME", String,
227
+ "The concurrency model to use, either#{nl}" +
228
+ "'process' or 'thread' (Enterprise only).#{nl}" +
229
+ "Default: #{DEFAULT_CONCURRENCY_MODEL}") do |value|
230
+ options[:concurrency_model] = value
231
+ end
232
+ opts.on("--thread-count NAME", Integer,
233
+ "The number of threads to use when using#{nl}" +
234
+ "the 'thread' concurrency model (Enterprise#{nl}" +
235
+ "only). Default: #{DEFAULT_APP_THREAD_COUNT}") do |value|
236
+ options[:thread_count] = value
237
+ end
238
+ opts.on("--rolling-restarts", "Enable rolling restarts (Enterprise only)") do
239
+ options[:rolling_restarts] = true
240
+ end
241
+ opts.on("--resist-deployment-errors", "Enable deployment error resistance#{nl}" +
242
+ "(Enterprise only)") do
243
+ options[:resist_deployment_errors] = true
244
+ end
245
+
246
+ opts.separator ""
247
+ opts.separator "Request handling options:"
248
+ opts.on("--sticky-sessions", "Enable sticky sessions") do
249
+ options[:sticky_sessions] = true
250
+ end
251
+ opts.on("--sticky-sessions-cookie-name NAME", String,
252
+ "Cookie name to use for sticky sessions.#{nl}" +
253
+ "Default: #{DEFAULT_STICKY_SESSIONS_COOKIE_NAME}") do |value|
254
+ options[:sticky_sessions_cookie_name] = value
255
+ end
256
+ opts.on("--vary-turbocache-by-cookie NAME", String,
257
+ "Vary the turbocache by the cookie of the given name") do |value|
258
+ options[:vary_turbocache_by_cookie] = value
259
+ end
260
+ opts.on("--disable-turbocaching", "Disable turbocaching") do
261
+ options[:turbocaching] = false
262
+ end
263
+
264
+ opts.separator ""
265
+ opts.separator "Union Station options:"
266
+ opts.on("--union-station-gateway HOST:PORT", String,
267
+ "Specify Union Station Gateway host and port") do |value|
268
+ host, port = value.split(":", 2)
269
+ port = port.to_i
270
+ port = 443 if port == 0
271
+ options[:union_station_gateway_address] = host
272
+ options[:union_station_gateway_port] = port.to_i
273
+ end
274
+ opts.on("--union-station-key KEY", String, "Specify Union Station key") do |value|
275
+ options[:union_station_key] = value
276
+ end
277
+
278
+ opts.separator ""
279
+ opts.separator "Nginx engine options:"
280
+ opts.on("--nginx-bin FILENAME", String, "Nginx binary to use as core") do |value|
281
+ options[:nginx_bin] = value
282
+ end
283
+ opts.on("--nginx-version VERSION", String,
284
+ "Nginx version to use as core.#{nl}" +
285
+ "Default: #{PREFERRED_NGINX_VERSION}") do |value|
286
+ options[:nginx_version] = value
287
+ end
288
+ opts.on("--nginx-tarball FILENAME", String,
289
+ "If Nginx needs to be installed, then the#{nl}" +
290
+ "given tarball will be used instead of#{nl}" +
291
+ "downloading from the Internet") do |value|
292
+ options[:nginx_tarball] = File.absolute_path_no_resolve(value)
293
+ end
294
+ opts.on("--nginx-config-template FILENAME", String,
295
+ "The template to use for generating the#{nl}" +
296
+ "Nginx config file") do |value|
297
+ options[:nginx_config_template] = File.absolute_path_no_resolve(value)
298
+ end
299
+
300
+ opts.separator ""
301
+ opts.separator "Advanced options:"
302
+ opts.on("--engine NAME", String,
303
+ "Underlying HTTP engine to use. Available#{nl}" +
304
+ "options: nginx (default), builtin") do |value|
305
+ options[:engine] = value
306
+ end
307
+ opts.on("--log-level NUMBER", Integer, "Log level to use. Default: #{DEFAULT_LOG_LEVEL}") do |value|
308
+ options[:log_level] = value
309
+ end
310
+ opts.on("--ctl NAME=VALUE", String) do |value|
311
+ if value !~ /=.+/
312
+ abort "*** ERROR: invalid --ctl format: #{value}"
313
+ end
314
+ options[:ctls] << value
315
+ end
316
+ opts.on("--binaries-url-root URL", String,
317
+ "If Nginx needs to be installed, then the#{nl}" +
318
+ "specified URL will be checked for binaries#{nl}" +
319
+ "prior to a local build") do |value|
320
+ options[:binaries_url_root] = value
321
+ end
322
+ opts.on("--no-download-binaries", "Never download binaries") do
323
+ options[:download_binaries] = false
324
+ end
325
+ opts.on("--runtime-check-only",
326
+ "Quit after checking whether the#{nl}" +
327
+ "#{PROGRAM_NAME} Standalone runtime files#{nl}" +
328
+ "are installed") do
329
+ options[:runtime_check_only] = true
330
+ end
331
+ opts.on("--no-install-runtime", "Abort if runtime must be installed") do
332
+ options[:dont_install_runtime] = true
333
+ end
334
+ opts.on("--no-compile-runtime", "Abort if runtime must be compiled") do
335
+ options[:dont_compile_runtime] = true
336
+ end
337
+ end
338
+ end
339
+
340
+ def load_local_config_file
341
+ if @argv.empty?
342
+ app_dir = File.absolute_path_no_resolve(".")
343
+ elsif @argv.size == 1
344
+ app_dir = @argv[0]
345
+ end
346
+ @local_options = {}
347
+ if app_dir
348
+ begin
349
+ ConfigUtils.load_local_config_file!(app_dir, @local_options)
350
+ rescue ConfigLoadError => e
351
+ abort "*** ERROR: #{e.message}"
352
+ end
353
+ end
354
+ end
355
+
356
+ # We want the command line options to override the options in the local config
357
+ # file, but the local config file could only be parsed when the command line
358
+ # options have been parsed. In this method we remerge all the config options
359
+ # from different sources so that options are overriden according to the following
360
+ # order:
361
+ #
362
+ # - DEFAULT_OPTIONS
363
+ # - global config file
364
+ # - local config file
365
+ # - command line options
366
+ def remerge_all_options
367
+ @options = DEFAULT_OPTIONS.
368
+ merge(@global_options).
369
+ merge(@local_options).
370
+ merge(@parsed_options)
371
+ end
372
+
373
+ def sanity_check_options_and_set_defaults
374
+ if (@options[:address] || @options[:port]) && @options[:socket_file]
375
+ abort "You cannot specify both --address/--port and --socket. Please choose either one."
376
+ end
377
+ if @options[:ssl] && !@options[:ssl_certificate]
378
+ abort "You specified --ssl. Please specify --ssl-certificate as well."
379
+ end
380
+ if @options[:ssl] && !@options[:ssl_certificate_key]
381
+ abort "You specified --ssl. Please specify --ssl-certificate-key as well."
382
+ end
383
+ if @options[:engine] != "builtin" && @options[:engine] != "nginx"
384
+ abort "You've specified an invalid value for --engine. The only values allowed are: builtin, nginx."
385
+ end
386
+
387
+ if !@options[:socket_file]
388
+ @options[:address] ||= "0.0.0.0"
389
+ @options[:port] ||= 3000
390
+ end
391
+
392
+ if @options[:engine] == "builtin"
393
+ # We explicitly check for some options are set and warn the user about this,
394
+ # in case they forget to pass --engine=nginx. We don't warn about options
395
+ # that begin with --nginx- because that should be obvious.
396
+ check_nginx_option_used_with_builtin_engine(:ssl, "--ssl")
397
+ check_nginx_option_used_with_builtin_engine(:ssl_certificate, "--ssl-certificate")
398
+ check_nginx_option_used_with_builtin_engine(:ssl_certificate_key, "--ssl-certificate-key")
399
+ check_nginx_option_used_with_builtin_engine(:ssl_port, "--ssl-port")
400
+ check_nginx_option_used_with_builtin_engine(:static_files_dir, "--static-files-dir")
401
+ end
402
+
403
+ #############
404
+ end
405
+
406
+ def check_nginx_option_used_with_builtin_engine(option, argument)
407
+ if @options.has_key?(option)
408
+ STDERR.puts "*** Warning: the #{argument} option is only allowed if you use " +
409
+ "the 'nginx' engine. You are currently using the 'builtin' engine, so " +
410
+ "this option has been ignored. To switch to the Nginx engine, please " +
411
+ "pass --engine=nginx."
412
+ end
413
+ end
414
+
415
+ def lookup_runtime_and_ensure_installed
416
+ @agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE)
417
+ if @options[:nginx_bin]
418
+ @nginx_binary = @options[:nginx_bin]
419
+ if !@nginx_binary
420
+ abort "*** ERROR: Nginx binary #{@options[:nginx_bin]} does not exist"
421
+ end
422
+ if !@agent_exe
423
+ install_runtime
424
+ @agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE)
425
+ end
426
+ else
427
+ nginx_name = "nginx-#{@options[:nginx_version]}"
428
+ @nginx_binary = PhusionPassenger.find_support_binary(nginx_name)
429
+ if !@agent_exe || !@nginx_binary
430
+ install_runtime
431
+ @agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE)
432
+ @nginx_binary = PhusionPassenger.find_support_binary(nginx_name)
433
+ end
434
+ end
435
+ end
436
+
437
+ def install_runtime
438
+ if @options[:dont_install_runtime]
439
+ abort "*** ERROR: Refusing to install the #{PROGRAM_NAME} Standalone runtime " +
440
+ "because --no-install-runtime is given."
441
+ end
442
+
443
+ args = [
444
+ "--brief",
445
+ "--no-force-tip",
446
+ # Use default Utils::Download timeouts, which are short. We want short
447
+ # timeouts so that if the primary server is down and is not responding
448
+ # (as opposed to responding quickly with an error), then the system
449
+ # quickly switches to a mirror.
450
+ "--connect-timeout", "0",
451
+ "--idle-timeout", "0"
452
+ ]
453
+ if @options[:binaries_url_root]
454
+ args << "--url-root"
455
+ args << @options[:binaries_url_root]
456
+ end
457
+ if @options[:nginx_version]
458
+ args << "--nginx-version"
459
+ args << @options[:nginx_version]
460
+ end
461
+ if @options[:nginx_tarball]
462
+ args << "--nginx-tarball"
463
+ args << @options[:nginx_tarball]
464
+ end
465
+ if @options[:dont_compile_runtime]
466
+ args << "--no-compile"
467
+ end
468
+ PhusionPassenger.require_passenger_lib 'config/install_standalone_runtime_command'
469
+ PhusionPassenger::Config::InstallStandaloneRuntimeCommand.new(args).run
470
+ puts
471
+ puts "--------------------------"
472
+ puts
473
+ end
474
+
475
+ def set_stdout_stderr_binmode
476
+ # We already set STDOUT and STDERR to binmode in bin/passenger, which
477
+ # fixes https://github.com/phusion/passenger-ruby-heroku-demo/issues/11.
478
+ # However RuntimeInstaller sets them to UTF-8, so here we set them back.
479
+ STDOUT.binmode
480
+ STDERR.binmode
481
+ end
482
+
483
+
484
+ ################## Core logic ##################
485
+
486
+ def find_apps
487
+ PhusionPassenger.require_passenger_lib 'standalone/app_finder'
488
+ @app_finder = AppFinder.new(@argv, @options)
489
+ @apps = @app_finder.scan
490
+ if @app_finder.multi_mode? && @options[:engine] != 'nginx'
491
+ puts "Mass deployment enabled, so forcing engine to 'nginx'."
492
+ @options[:engine] = 'nginx'
493
+ end
494
+ end
495
+
496
+ def find_pid_and_log_file(app_finder, options)
497
+ exec_root = app_finder.execution_root
498
+ if options[:socket_file]
499
+ pid_basename = "passenger.pid"
500
+ log_basename = "passenger.log"
501
+ else
502
+ pid_basename = "passenger.#{options[:port]}.pid"
503
+ log_basename = "passenger.#{options[:port]}.log"
504
+ end
505
+ if File.directory?("#{exec_root}/tmp/pids")
506
+ options[:pid_file] ||= "#{exec_root}/tmp/pids/#{pid_basename}"
507
+ else
508
+ options[:pid_file] ||= "#{exec_root}/#{pid_basename}"
509
+ end
510
+ if File.directory?("log")
511
+ options[:log_file] ||= "#{exec_root}/log/#{log_basename}"
512
+ else
513
+ options[:log_file] ||= "#{exec_root}/#{log_basename}"
514
+ end
515
+ end
516
+
517
+ def create_working_dir
518
+ # We don't remove the working dir in 'passenger start'. In daemon
519
+ # mode 'passenger start' just quits and lets background processes
520
+ # running. A specific background process, temp-dir-toucher, is
521
+ # responsible for cleaning up the working dir.
522
+ @working_dir = PhusionPassenger::Utils.mktmpdir("passenger-standalone.")
523
+ File.chmod(0755, @working_dir)
524
+ Dir.mkdir("#{@working_dir}/logs")
525
+ @can_remove_working_dir = true
526
+ end
527
+
528
+ def initialize_vars
529
+ @console_mutex = Mutex.new
530
+ @termination_pipe = IO.pipe
531
+ @threads = []
532
+ @interruptable_threads = []
533
+ end
534
+
535
+ def start_engine
536
+ metaclass = class << self; self; end
537
+ if @options[:engine] == 'nginx'
538
+ PhusionPassenger.require_passenger_lib 'standalone/start_command/nginx_engine'
539
+ metaclass.send(:include, PhusionPassenger::Standalone::StartCommand::NginxEngine)
540
+ else
541
+ PhusionPassenger.require_passenger_lib 'standalone/start_command/builtin_engine'
542
+ metaclass.send(:include, PhusionPassenger::Standalone::StartCommand::BuiltinEngine)
543
+ end
544
+ start_engine_real
545
+ end
546
+
547
+ # Returns the URL that the server will be listening on.
548
+ def listen_url
549
+ if @options[:socket_file]
550
+ return @options[:socket_file]
551
+ else
552
+ if @options[:ssl] && !@options[:ssl_port]
553
+ scheme = "https"
554
+ else
555
+ scheme = "http"
556
+ end
557
+ result = "#{scheme}://"
558
+ if @options[:port] == 80
559
+ result << @options[:address]
560
+ else
561
+ result << compose_ip_and_port(@options[:address], @options[:port])
562
+ end
563
+ result << "/"
564
+ return result
565
+ end
566
+ end
567
+
568
+ def compose_ip_and_port(ip, port)
569
+ if ip =~ /:/
570
+ # IPv6
571
+ return "[#{ip}]:#{port}"
572
+ else
573
+ return "#{ip}:#{port}"
574
+ end
575
+ end
576
+
577
+ def show_intro_message
578
+ puts "=============== Phusion Passenger Standalone web server started ==============="
579
+ puts "PID file: #{@options[:pid_file]}"
580
+ puts "Log file: #{@options[:log_file]}"
581
+ puts "Environment: #{@options[:environment]}"
582
+ puts "Accessible via: #{listen_url}"
583
+
584
+ puts
585
+ if @options[:daemonize]
586
+ puts "Serving in the background as a daemon."
587
+ else
588
+ puts "You can stop #{PROGRAM_NAME} Standalone by pressing Ctrl-C."
589
+ end
590
+ puts "Problems? Check #{STANDALONE_DOC_URL}#troubleshooting"
591
+ puts "==============================================================================="
592
+ end
593
+
594
+ def maybe_daemonize
595
+ if @options[:daemonize]
596
+ PhusionPassenger.require_passenger_lib 'platform_info/ruby'
597
+ if PlatformInfo.ruby_supports_fork?
598
+ daemonize
599
+ else
600
+ abort "Unable to daemonize using the current Ruby interpreter " +
601
+ "(#{PlatformInfo.ruby_command}) because it does not support forking."
602
+ end
603
+ end
604
+ end
605
+
606
+ def daemonize
607
+ pid = fork
608
+ if pid
609
+ # Parent
610
+ exit!(0)
611
+ else
612
+ # Child
613
+ trap "HUP", "IGNORE"
614
+ STDIN.reopen("/dev/null", "r")
615
+ STDOUT.reopen(@options[:log_file], "a")
616
+ STDERR.reopen(@options[:log_file], "a")
617
+ STDOUT.sync = true
618
+ STDERR.sync = true
619
+ Process.setsid
620
+ @threads.clear
621
+ end
622
+ end
623
+
624
+ def touch_temp_dir_in_background
625
+ result = system(@agent_exe,
626
+ "temp-dir-toucher",
627
+ @working_dir,
628
+ "--cleanup",
629
+ "--daemonize",
630
+ "--pid-file", "#{@working_dir}/temp_dir_toucher.pid",
631
+ "--log-file", @options[:log_file])
632
+ if !result
633
+ abort "Cannot start #{@agent_exe} temp-dir-toucher"
634
+ end
635
+ @can_remove_working_dir = false
636
+ end
637
+
638
+ def watch_log_file(log_file)
639
+ if File.exist?(log_file)
640
+ backward = 0
641
+ else
642
+ # tail bails out if the file doesn't exist, so wait until it exists.
643
+ while !File.exist?(log_file)
644
+ sleep 1
645
+ end
646
+ backward = 10
647
+ end
648
+
649
+ IO.popen("tail -f -n #{backward} \"#{log_file}\"", "rb") do |f|
650
+ begin
651
+ while true
652
+ begin
653
+ line = f.readline
654
+ @console_mutex.synchronize do
655
+ STDOUT.write(line)
656
+ STDOUT.flush
657
+ end
658
+ rescue EOFError
659
+ break
660
+ end
661
+ end
662
+ ensure
663
+ Process.kill('TERM', f.pid) rescue nil
664
+ end
665
+ end
666
+ end
667
+
668
+ def watch_log_files_in_background
669
+ @apps.each do |app|
670
+ thread = Utils.create_thread_and_abort_on_exception do
671
+ watch_log_file("#{app[:root]}/log/#{@options[:environment]}.log")
672
+ end
673
+ @threads << thread
674
+ @interruptable_threads << thread
675
+ end
676
+ thread = Utils.create_thread_and_abort_on_exception do
677
+ watch_log_file(@options[:log_file])
678
+ end
679
+ @threads << thread
680
+ @interruptable_threads << thread
681
+ end
682
+
683
+ def should_watch_logs?
684
+ return !@options[:daemonize] && @options[:log_file] != "/dev/null"
685
+ end
686
+
687
+ def should_wait_until_engine_has_exited?
688
+ return !@options[:daemonize] || @app_finder.multi_mode?
689
+ end
690
+
691
+
692
+ ################## Shut down and cleanup ##################
693
+
694
+ def shutdown_and_cleanup(error_occurred)
695
+ # Stop engine
696
+ if @engine && (error_occurred || should_wait_until_engine_has_exited?)
697
+ @console_mutex.synchronize do
698
+ STDOUT.write("Stopping web server...")
699
+ STDOUT.flush
700
+ @engine.stop
701
+ STDOUT.puts " done"
702
+ STDOUT.flush
703
+ end
704
+ @engine = nil
705
+ end
706
+
707
+ # Stop threads
708
+ if @threads
709
+ if !@termination_pipe[1].closed?
710
+ @termination_pipe[1].write("x")
711
+ @termination_pipe[1].close
712
+ end
713
+ @interruptable_threads.each do |thread|
714
+ thread.terminate
715
+ end
716
+ @interruptable_threads = []
717
+ @threads.each do |thread|
718
+ thread.join
719
+ end
720
+ @threads = nil
721
+ end
722
+
723
+ if @can_remove_working_dir
724
+ FileUtils.remove_entry_secure(@working_dir)
725
+ @can_remove_working_dir = false
726
+ end
727
+ end
728
+
729
+ #################
730
+ end
731
+
732
+ end # module Standalone
721
733
  end # module PhusionPassenger