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,772 +29,772 @@ PhusionPassenger.require_passenger_lib 'platform_info/linux'
29
29
 
30
30
  module PhusionPassenger
31
31
 
32
- # Wow, I can't believe in how many ways one can build Apache in OS
33
- # X! We have to resort to all sorts of tricks to make Passenger build
34
- # out of the box on OS X. :-(
35
- #
36
- # In the name of usability and the "end user is the king" line of thought,
37
- # I shall suffer the horrible faith of writing tons of autodetection code!
38
-
39
- # Users can change the detection behavior by setting the environment variable
40
- # <tt>APXS2</tt> to the correct 'apxs' (or 'apxs2') binary, as provided by
41
- # Apache.
42
-
43
- module PlatformInfo
44
- ################ Programs ################
45
-
46
- # The absolute path to the 'apxs' or 'apxs2' executable, or nil if not found.
47
- def self.apxs2
48
- if env_defined?("APXS2")
49
- return ENV["APXS2"]
50
- end
51
- ['apxs2', 'apxs'].each do |name|
52
- command = find_command(name)
53
- if !command.nil?
54
- return command
55
- end
56
- end
57
- return nil
58
- end
59
- memoize :apxs2
60
-
61
- # The absolute path to the 'apachectl' or 'apache2ctl' binary, or nil if
62
- # not found.
63
- def self.apache2ctl(options = {})
64
- return find_apache2_executable('apache2ctl', 'apachectl2', 'apachectl', options)
65
- end
66
- memoize :apache2ctl
67
-
68
- # The absolute path to the Apache binary (that is, 'httpd', 'httpd2', 'apache'
69
- # or 'apache2'), or nil if not found.
70
- def self.httpd(options = {})
71
- apxs2 = options[:apxs2] || self.apxs2
72
- if env_defined?('HTTPD')
73
- return ENV['HTTPD']
74
- elsif apxs2.nil?
75
- ["apache2", "httpd2", "apache", "httpd"].each do |name|
76
- command = find_command(name)
77
- if !command.nil?
78
- return command
79
- end
80
- end
81
- return nil
82
- else
83
- return find_apache2_executable(`#{apxs2} -q TARGET`.strip, options)
84
- end
85
- end
86
- memoize :httpd
87
-
88
- # The Apache version, or nil if Apache is not found.
89
- def self.httpd_version(options = nil)
90
- if options
91
- httpd = options[:httpd] || self.httpd(options)
92
- else
93
- httpd = self.httpd
94
- end
95
- if httpd
96
- `#{httpd} -v` =~ %r{Apache/([\d\.]+)}
97
- return $1
98
- else
99
- return nil
100
- end
101
- end
102
- memoize :httpd_version
103
-
104
- # Run `httpd -V` and return its output. On some systems, such as Ubuntu 13.10,
105
- # `httpd -V` fails without the environment variables defined in various scripts.
106
- # Here we take care of evaluating those scripts before running `httpd -V`.
107
- def self.httpd_V(options = nil)
108
- if options
109
- httpd = options[:httpd] || self.httpd(options)
110
- else
111
- httpd = self.httpd
112
- end
113
- if httpd
114
- command = "#{httpd} -V"
115
- if envvars_file = httpd_envvars_file(options)
116
- command = ". '#{envvars_file}' && #{command}"
117
- end
118
- return `#{command}`
119
- else
120
- return nil
121
- end
122
- end
123
- memoize :httpd_V
124
-
125
- # The Apache executable's architectural bits. Returns 32 or 64,
126
- # or nil if unable to detect.
127
- def self.httpd_architecture_bits(options = nil)
128
- if options
129
- httpd = options[:httpd] || self.httpd(options)
130
- else
131
- httpd = self.httpd
132
- end
133
- if httpd
134
- `#{httpd} -V` =~ %r{Architecture:(.*)}
135
- text = $1
136
- if text =~ /32/
137
- return 32
138
- elsif text =~ /64/
139
- return 64
140
- else
141
- return nil
142
- end
143
- else
144
- return nil
145
- end
146
- end
147
- memoize :httpd_architecture_bits
148
-
149
- # The default Apache root directory, as specified by its compilation parameters.
150
- # This may be different from the value of the ServerRoot directive.
151
- def self.httpd_default_root(options = nil)
152
- if options
153
- info = httpd_V(options)
154
- else
155
- info = httpd_V
156
- end
157
- if info
158
- info =~ / -D HTTPD_ROOT="(.+)"$/
159
- return $1
160
- else
161
- return nil
162
- end
163
- end
164
- memoize :httpd_default_root
165
-
166
- # The default Apache configuration file, or nil if Apache is not found.
167
- def self.httpd_default_config_file(options = nil)
168
- if options
169
- info = httpd_V(options)
170
- else
171
- info = httpd_V
172
- end
173
- if info
174
- info =~ /-D SERVER_CONFIG_FILE="(.+)"$/
175
- filename = $1
176
- if filename =~ /\A\//
177
- return filename
178
- else
179
- # Not an absolute path. Infer from default root.
180
- if root = httpd_default_root(options)
181
- return "#{root}/#{filename}"
182
- else
183
- return nil
184
- end
185
- end
186
- else
187
- return nil
188
- end
189
- end
190
- memoize :httpd_default_config_file
191
-
192
- # Given an Apache config file, returns the a hash with the following elements:
193
- #
194
- # * `:files` - An array containing `config_file`, as well as all config files
195
- # included from that config file, including recursively included
196
- # ones. Only filenames that actually exist are put here.
197
- # * `:unreadable_files` - All config files that this function was unable
198
- # to read.
199
- def self.httpd_included_config_files(config_file, options = nil)
200
- state = {
201
- :files => { config_file => true },
202
- :unreadable_files => [],
203
- :root => httpd_default_root(options)
204
- }
205
- scan_for_included_apache2_config_files(config_file, state, options)
206
- return {
207
- :files => state[:files].keys,
208
- :unreadable_files => state[:unreadable_files]
209
- }
210
- end
211
-
212
- # The default Apache error log's filename, as it is compiled into the Apache
213
- # main executable. This may not be the actual error log that is used. The actual
214
- # error log depends on the configuration file.
215
- #
216
- # Returns nil if Apache is not detected, or if the default error log filename
217
- # cannot be detected.
218
- def self.httpd_default_error_log(options = nil)
219
- if info = httpd_V(options)
220
- info =~ /-D DEFAULT_ERRORLOG="(.+)"$/
221
- filename = $1
222
- if filename =~ /\A\//
223
- return filename
224
- else
225
- # Not an absolute path. Infer from default root.
226
- if root = httpd_default_root(options)
227
- return "#{root}/#{filename}"
228
- else
229
- return nil
230
- end
231
- end
232
- else
233
- return nil
234
- end
235
- end
236
- memoize :httpd_default_error_log
237
-
238
- def self.httpd_actual_error_log(options = nil)
239
- if config_file = httpd_default_config_file(options)
240
- begin
241
- contents = File.open(config_file, "rb") { |f| f.read }
242
- rescue Errno::ENOENT
243
- log "#{config_file} does not exist"
244
- return nil
245
- rescue Errno::EACCES
246
- log "Unable to open #{config_file} for reading"
247
- return nil
248
- end
249
- # We don't want to match comments
250
- contents.gsub!(/^[ \t]*#.*/, '')
251
- if contents =~ /^[ \t]*ErrorLog[ \t]+(.+)[ \t]*$/i
252
- filename = unescape_apache_config_value($1, options)
253
- if filename && filename !~ /\A\//
254
- # Not an absolute path. Infer from root.
255
- if root = httpd_default_root(options)
256
- return "#{root}/#{filename}"
257
- else
258
- return nil
259
- end
260
- else
261
- return filename
262
- end
263
- elsif contents =~ /ErrorLog/i
264
- # The user apparently has ErrorLog set somewhere but
265
- # we can't parse it. The default error log location,
266
- # as reported by `httpd -V`, may be wrong (it is on OS X).
267
- # So to be safe, let's assume that we don't know.
268
- log "Unable to parse ErrorLog directive in Apache configuration file"
269
- return nil
270
- else
271
- log "No ErrorLog directive in Apache configuration file"
272
- return httpd_default_error_log(options)
273
- end
274
- else
275
- return nil
276
- end
277
- end
278
- memoize :httpd_actual_error_log
279
-
280
- # The location of the Apache envvars file, which exists on some systems such as Ubuntu.
281
- # Returns nil if Apache is not found or if the envvars file is not found.
282
- def self.httpd_envvars_file(options = nil)
283
- if options
284
- httpd = options[:httpd] || self.httpd(options)
285
- else
286
- httpd = self.httpd
287
- end
288
-
289
- httpd_dir = File.dirname(httpd)
290
- if httpd_dir == "/usr/bin" || httpd_dir == "/usr/sbin"
291
- if File.exist?("/etc/apache2/envvars")
292
- return "/etc/apache2/envvars"
293
- elsif File.exist?("/etc/httpd/envvars")
294
- return "/etc/httpd/envvars"
295
- end
296
- end
297
-
298
- conf_dir = File.expand_path(File.dirname(httpd) + "/../conf")
299
- if File.exist?("#{conf_dir}/envvars")
300
- return "#{conf_dir}/envvars"
301
- end
302
-
303
- return nil
304
- end
305
-
306
- def self.httpd_infer_envvar(varname, options = nil)
307
- if envfile = httpd_envvars_file(options)
308
- result = `. '#{envfile}' && echo $#{varname}`.strip
309
- if $? && $?.exitstatus == 0
310
- return result
311
- else
312
- return nil
313
- end
314
- else
315
- return nil
316
- end
317
- end
318
-
319
- # Returns the path to the Apache `mods-available` subdirectory,
320
- # or nil if it's not supported by this Apache.
321
- def self.httpd_mods_available_directory(options = nil)
322
- config_file = httpd_default_config_file(options)
323
- return nil if !config_file
324
-
325
- # mods-available is supposed to be a Debian extension that only works
326
- # on the APT-installed Apache, so only return non-nil if we're
327
- # working against the APT-installed Apache.
328
- config_dir = File.dirname(config_file)
329
- if config_dir == "/etc/httpd" || config_dir == "/etc/apache2"
330
- if File.exist?("#{config_dir}/mods-available") &&
331
- File.exist?("#{config_dir}/mods-enabled")
332
- return "#{config_dir}/mods-available"
333
- else
334
- return nil
335
- end
336
- else
337
- return nil
338
- end
339
- end
340
- memoize :httpd_mods_available_directory
341
-
342
- # Returns the path to the Apache `mods-enabled` subdirectory,
343
- # or nil if it's not supported by this Apache.
344
- def self.httpd_mods_enabled_directory(options = nil)
345
- config_file = httpd_default_config_file(options)
346
- return nil if !config_file
347
-
348
- # mods-enabled is supposed to be a Debian extension that only works
349
- # on the APT-installed Apache, so only return non-nil if we're
350
- # working against the APT-installed Apache.
351
- config_dir = File.dirname(config_file)
352
- if config_dir == "/etc/httpd" || config_dir == "/etc/apache2"
353
- if File.exist?("#{config_dir}/mods-available") &&
354
- File.exist?("#{config_dir}/mods-enabled")
355
- return "#{config_dir}/mods-enabled"
356
- else
357
- return nil
358
- end
359
- else
360
- return nil
361
- end
362
- end
363
- memoize :httpd_mods_enabled_directory
364
-
365
- # The absolute path to the 'a2enmod' executable.
366
- def self.a2enmod(options = {})
367
- apxs2 = options[:apxs2] || self.apxs2
368
- dir = File.dirname(apxs2)
369
- # a2enmod is supposed to be a Debian extension that only works
370
- # on the APT-installed Apache, so only return non-nil if we're
371
- # working against the APT-installed Apache.
372
- if dir == "/usr/bin" || dir == "/usr/sbin"
373
- if env_defined?('A2ENMOD')
374
- return ENV['A2ENMOD']
375
- else
376
- return find_apache2_executable("a2enmod", options)
377
- end
378
- else
379
- return nil
380
- end
381
- end
382
- memoize :a2enmod
383
-
384
- # The absolute path to the 'a2enmod' executable.
385
- def self.a2dismod(options = {})
386
- apxs2 = options[:apxs2] || self.apxs2
387
- dir = File.dirname(apxs2)
388
- # a2dismod is supposed to be a Debian extension that only works
389
- # on the APT-installed Apache, so only return non-nil if we're
390
- # working against the APT-installed Apache.
391
- if dir == "/usr/bin" || dir == "/usr/sbin"
392
- if env_defined?('A2DISMOD')
393
- return ENV['A2DISMOD']
394
- else
395
- return find_apache2_executable("a2dismod", options)
396
- end
397
- end
398
- end
399
- memoize :a2dismod
400
-
401
- # The absolute path to the 'apr-config' or 'apr-1-config' executable,
402
- # or nil if not found.
403
- def self.apr_config
404
- if env_defined?('APR_CONFIG')
405
- return ENV['APR_CONFIG']
406
- elsif apxs2.nil?
407
- return nil
408
- else
409
- filename = `#{apxs2} -q APR_CONFIG 2>/dev/null`.strip
410
- if filename.empty?
411
- apr_bindir = `#{apxs2} -q APR_BINDIR 2>/dev/null`.strip
412
- if apr_bindir.empty?
413
- return nil
414
- else
415
- return select_executable(apr_bindir,
416
- "apr-1-config", "apr-config")
417
- end
418
- elsif File.exist?(filename)
419
- return filename
420
- else
421
- return nil
422
- end
423
- end
424
- end
425
- memoize :apr_config
426
-
427
- # The absolute path to the 'apu-config' or 'apu-1-config' executable, or nil
428
- # if not found.
429
- def self.apu_config
430
- if env_defined?('APU_CONFIG')
431
- return ENV['APU_CONFIG']
432
- elsif apxs2.nil?
433
- return nil
434
- else
435
- filename = `#{apxs2} -q APU_CONFIG 2>/dev/null`.strip
436
- if filename.empty?
437
- apu_bindir = `#{apxs2} -q APU_BINDIR 2>/dev/null`.strip
438
- if apu_bindir.empty?
439
- return nil
440
- else
441
- return select_executable(apu_bindir,
442
- "apu-1-config", "apu-config")
443
- end
444
- elsif File.exist?(filename)
445
- return filename
446
- else
447
- return nil
448
- end
449
- end
450
- end
451
- memoize :apu_config
452
-
453
- # Find an executable in the Apache 'bin' and 'sbin' directories.
454
- # Returns nil if not found.
455
- def self.find_apache2_executable(*possible_names)
456
- if possible_names.last.is_a?(Hash)
457
- options = possible_names.pop
458
- options = nil if options.empty?
459
- end
460
-
461
- if options
462
- dirs = options[:dirs] || [apache2_bindir(options), apache2_sbindir(options)]
463
- else
464
- dirs = [apache2_bindir, apache2_sbindir]
465
- end
466
-
467
- dirs.each do |bindir|
468
- if bindir.nil?
469
- next
470
- end
471
- possible_names.each do |name|
472
- filename = "#{bindir}/#{name}"
473
- if !File.exist?(filename)
474
- log "Looking for #{filename}: not found"
475
- elsif !File.file?(filename)
476
- log "Looking for #{filename}: found, but is not a file"
477
- elsif !File.executable?(filename)
478
- log "Looking for #{filename}: found, but is not executable"
479
- else
480
- log "Looking for #{filename}: found"
481
- return filename
482
- end
483
- end
484
- end
485
- return nil
486
- end
487
-
488
-
489
- ################ Directories ################
490
-
491
- # The absolute path to the Apache 2 'bin' directory, or nil if unknown.
492
- def self.apache2_bindir(options = {})
493
- apxs2 = options[:apxs2] || self.apxs2
494
- if apxs2.nil?
495
- return nil
496
- else
497
- return `#{apxs2} -q BINDIR 2>/dev/null`.strip
498
- end
499
- end
500
- memoize :apache2_bindir
501
-
502
- # The absolute path to the Apache 2 'sbin' directory, or nil if unknown.
503
- def self.apache2_sbindir(options = {})
504
- apxs2 = options[:apxs2] || self.apxs2
505
- if apxs2.nil?
506
- return nil
507
- else
508
- return `#{apxs2} -q SBINDIR`.strip
509
- end
510
- end
511
- memoize :apache2_sbindir
512
-
513
-
514
- ################ Compiler and linker flags ################
515
-
516
- def self.apache2_module_cflags(with_apr_flags = true)
517
- return apache2_module_c_or_cxxflags(:c, with_apr_flags)
518
- end
519
- memoize :apache2_module_cflags, true
520
-
521
- def self.apache2_module_cxxflags(with_apr_flags = true)
522
- return apache2_module_c_or_cxxflags(:cxx, with_apr_flags)
523
- end
524
- memoize :apache2_module_cxxflags, true
525
-
526
- # The C compiler flags that are necessary to compile an Apache module.
527
- # Also includes APR and APU compiler flags if with_apr_flags is true.
528
- def self.apache2_module_c_or_cxxflags(language, with_apr_flags = true)
529
- flags = [""]
530
- if (language == :c && cc_is_sun_studio?) || (language == :cxx && cxx_is_sun_studio?)
531
- flags << "-KPIC"
532
- else
533
- flags << "-fPIC"
534
- end
535
- if with_apr_flags
536
- flags << apr_flags
537
- flags << apu_flags
538
- end
539
- if !apxs2.nil?
540
- apxs2_flags = `#{apxs2} -q CFLAGS`.strip << " -I" << `#{apxs2} -q INCLUDEDIR`.strip
541
- apxs2_flags.gsub!(/-O\d? /, '')
542
-
543
- # Remove flags not supported by GCC
544
- if os_name =~ /solaris/ # TODO: Add support for people using SunStudio
545
- # The big problem is Coolstack apxs includes a bunch of solaris -x directives.
546
- options = apxs2_flags.split
547
- options.reject! { |f| f =~ /^\-x/ }
548
- options.reject! { |f| f =~ /^\-Xa/ }
549
- options.reject! { |f| f =~ /^\-fast/ }
550
- options.reject! { |f| f =~ /^\-mt/ }
551
- apxs2_flags = options.join(' ')
552
- end
553
-
554
- if os_name == "linux" &&
555
- linux_distro_tags.include?(:redhat) &&
556
- apxs2 == "/usr/sbin/apxs" &&
557
- httpd_architecture_bits == 64
558
- # The Apache package in CentOS 5 x86_64 is broken.
559
- # 'apxs -q CFLAGS' contains directives for compiling
560
- # the module as 32-bit, even though httpd itself
561
- # is 64-bit. Fix this.
562
- apxs2_flags.gsub!('-m32 -march=i386 -mtune=generic', '')
563
- end
564
-
565
- apxs2_flags.strip!
566
- flags << apxs2_flags
567
- end
568
- if !httpd.nil? && os_name == "macosx"
569
- # The default Apache install on OS X is a universal binary.
570
- # Figure out which architectures it's compiled for and do the same
571
- # thing for mod_passenger. We use the 'file' utility to do this.
572
- #
573
- # Running 'file' on the Apache executable usually outputs something
574
- # like this:
575
- #
576
- # /usr/sbin/httpd: Mach-O universal binary with 4 architectures
577
- # /usr/sbin/httpd (for architecture ppc7400): Mach-O executable ppc
578
- # /usr/sbin/httpd (for architecture ppc64): Mach-O 64-bit executable ppc64
579
- # /usr/sbin/httpd (for architecture i386): Mach-O executable i386
580
- # /usr/sbin/httpd (for architecture x86_64): Mach-O 64-bit executable x86_64
581
- #
582
- # But on some machines, it may output just:
583
- #
584
- # /usr/sbin/httpd: Mach-O fat file with 4 architectures
585
- #
586
- # (http://code.google.com/p/phusion-passenger/issues/detail?id=236)
587
- output = `file "#{httpd}"`.strip
588
- if output =~ /Mach-O fat file/ && output !~ /for architecture/
589
- architectures = ["i386", "ppc", "x86_64", "ppc64"]
590
- else
591
- architectures = []
592
- output.split("\n").grep(/for architecture/).each do |line|
593
- line =~ /for architecture (.*?)\)/
594
- architectures << $1
595
- end
596
- end
597
- # The compiler may not support all architectures in the binary.
598
- # XCode 4 seems to have removed support for the PPC architecture
599
- # even though there are still plenty of Apache binaries around
600
- # containing PPC components.
601
- architectures.reject! do |arch|
602
- !compiler_supports_architecture?(arch)
603
- end
604
- architectures.map! do |arch|
605
- "-arch #{arch}"
606
- end
607
- flags << architectures.compact.join(' ')
608
- end
609
- return flags.compact.join(' ').strip
610
- end
611
-
612
- # Linker flags that are necessary for linking an Apache module.
613
- # Already includes APR and APU linker flags.
614
- def self.apache2_module_cxx_ldflags
615
- if cxx_is_sun_studio?
616
- flags = "-KPIC"
617
- else
618
- flags = "-fPIC"
619
- end
620
- flags << " #{apr_libs} #{apu_libs}"
621
- flags.strip!
622
- return flags
623
- end
624
- memoize :apache2_module_cxx_ldflags
625
-
626
- # The C compiler flags that are necessary for programs that use APR.
627
- def self.apr_flags
628
- return determine_apr_info[0]
629
- end
630
-
631
- # The linker flags that are necessary for linking programs that use APR.
632
- def self.apr_libs
633
- return determine_apr_info[1]
634
- end
635
-
636
- # The C compiler flags that are necessary for programs that use APR-Util.
637
- def self.apu_flags
638
- return determine_apu_info[0]
639
- end
640
-
641
- # The linker flags that are necessary for linking programs that use APR-Util.
642
- def self.apu_libs
643
- return determine_apu_info[1]
644
- end
645
-
646
- ################ Miscellaneous information ################
647
-
648
-
649
- # Returns whether it is necessary to use information outputted by
650
- # 'apr-config' and 'apu-config' in order to compile an Apache module.
651
- # When Apache is installed with --with-included-apr, the APR/APU
652
- # headers are placed into the same directory as the Apache headers,
653
- # and so 'apr-config' and 'apu-config' won't be necessary in that case.
654
- def self.apr_config_needed_for_building_apache_modules?
655
- return !try_compile("whether APR is needed for building Apache modules",
656
- :c, "#include <apr.h>\n", apache2_module_cflags(false))
657
- end
658
- memoize :apr_config_needed_for_building_apache_modules?, true
659
-
660
- private
661
- def self.determine_apr_info
662
- if apr_config.nil?
663
- return [nil, nil]
664
- else
665
- flags = `#{apr_config} --cppflags --includes`.strip
666
- libs = `#{apr_config} --link-ld`.strip
667
- flags.gsub!(/-O\d? /, '')
668
- if os_name =~ /solaris/
669
- # Remove flags not supported by GCC
670
- flags = flags.split(/ +/).reject{ |f| f =~ /^\-mt/ }.join(' ')
671
- elsif os_name =~ /aix/
672
- libs << " -Wl,-G -Wl,-brtl"
673
- end
674
- return [flags, libs]
675
- end
676
- end
677
- memoize :determine_apr_info, true
678
- private_class_method :determine_apr_info
679
-
680
- def self.determine_apu_info
681
- if apu_config.nil?
682
- return [nil, nil]
683
- else
684
- flags = `#{apu_config} --includes`.strip
685
- libs = `#{apu_config} --link-ld`.strip
686
- flags.gsub!(/-O\d? /, '')
687
- return [flags, libs]
688
- end
689
- end
690
- memoize :determine_apu_info, true
691
- private_class_method :determine_apu_info
692
-
693
- def self.scan_for_included_apache2_config_files(config_file, state, options = nil)
694
- begin
695
- config = File.open(config_file, "rb") do |f|
696
- f.read
697
- end
698
- rescue Errno::EACCES
699
- state[:unreadable_files] << config_file
700
- return
701
- end
702
-
703
- found_filenames = []
704
-
705
- config.scan(/^[ \t]*(Include(Optional)?|ServerRoot)[ \t]+(.+?)[ \t]*$/i) do |match|
706
- if match[0].downcase == "serverroot"
707
- new_root = unescape_apache_config_value(match[2], options)
708
- state[:root] = new_root if new_root
709
- else
710
- filename = unescape_apache_config_value(match[2], options)
711
- next if filename.nil? || filename.empty?
712
- if filename !~ /\A\//
713
- # Not an absolute path. Infer from root.
714
- filename = "#{state[:root]}/#{filename}"
715
- end
716
- expand_apache2_glob(filename).each do |filename2|
717
- if !state[:files].has_key?(filename2)
718
- state[:files][filename2] = true
719
- scan_for_included_apache2_config_files(filename2, state, options)
720
- end
721
- end
722
- end
723
- end
724
- end
725
- private_class_method :scan_for_included_apache2_config_files
726
-
727
- def self.expand_apache2_glob(glob)
728
- if File.directory?(glob)
729
- glob = glob.sub(/\/*$/, '')
730
- result = Dir["#{glob}/**/*"]
731
- else
732
- result = []
733
- Dir[glob].each do |filename|
734
- if File.directory?(filename)
735
- result.concat(Dir["#{filename}/**/*"])
736
- else
737
- result << filename
738
- end
739
- end
740
- end
741
- result.reject! do |filename|
742
- File.directory?(filename)
743
- end
744
- return result
745
- end
746
- private_class_method :expand_apache2_glob
747
-
748
- def self.unescape_apache_config_value(value, options = nil)
749
- if value =~ /^"(.*)"$/
750
- value = unescape_c_string($1)
751
- end
752
- if value.include?("${")
753
- log "Attempting to substitute environment variables in Apache config value #{value.inspect}..."
754
- end
755
- # The Apache config file supports environment variable
756
- # substitution. Ubuntu uses this extensively.
757
- value.gsub!(/\$\{(.+?)\}/) do |varname|
758
- if substitution = httpd_infer_envvar($1, options)
759
- log "Substituted \"#{varname}\" -> \"#{substitution}\""
760
- substitution
761
- else
762
- log "Cannot substitute \"#{varname}\""
763
- varname
764
- end
765
- end
766
- if value.include?("${")
767
- # We couldn't substitute everything.
768
- return nil
769
- else
770
- return value
771
- end
772
- end
773
- private_class_method :unescape_apache_config_value
774
-
775
- def self.unescape_c_string(s)
776
- state = 0
777
- res = ''
778
- backslash = "\\"
779
- s.each_char do |c|
780
- case state
781
- when 0
782
- case c
783
- when backslash then state = 1
784
- else res << c
785
- end
786
- when 1
787
- case c
788
- when 'n' then res << "\n"; state = 0
789
- when 't' then res << "\t"; state = 0
790
- when backslash then res << backslash; state = 0
791
- else res << backslash; res << c; state = 0
792
- end
793
- end
794
- end
795
- return res
796
- end
797
- private_class_method :unescape_c_string
798
- end
32
+ # Wow, I can't believe in how many ways one can build Apache in OS
33
+ # X! We have to resort to all sorts of tricks to make Passenger build
34
+ # out of the box on OS X. :-(
35
+ #
36
+ # In the name of usability and the "end user is the king" line of thought,
37
+ # I shall suffer the horrible faith of writing tons of autodetection code!
38
+
39
+ # Users can change the detection behavior by setting the environment variable
40
+ # <tt>APXS2</tt> to the correct 'apxs' (or 'apxs2') binary, as provided by
41
+ # Apache.
42
+
43
+ module PlatformInfo
44
+ ################ Programs ################
45
+
46
+ # The absolute path to the 'apxs' or 'apxs2' executable, or nil if not found.
47
+ def self.apxs2
48
+ if env_defined?("APXS2")
49
+ return ENV["APXS2"]
50
+ end
51
+ ['apxs2', 'apxs'].each do |name|
52
+ command = find_command(name)
53
+ if !command.nil?
54
+ return command
55
+ end
56
+ end
57
+ return nil
58
+ end
59
+ memoize :apxs2
60
+
61
+ # The absolute path to the 'apachectl' or 'apache2ctl' binary, or nil if
62
+ # not found.
63
+ def self.apache2ctl(options = {})
64
+ return find_apache2_executable('apache2ctl', 'apachectl2', 'apachectl', options)
65
+ end
66
+ memoize :apache2ctl
67
+
68
+ # The absolute path to the Apache binary (that is, 'httpd', 'httpd2', 'apache'
69
+ # or 'apache2'), or nil if not found.
70
+ def self.httpd(options = {})
71
+ apxs2 = options[:apxs2] || self.apxs2
72
+ if env_defined?('HTTPD')
73
+ return ENV['HTTPD']
74
+ elsif apxs2.nil?
75
+ ["apache2", "httpd2", "apache", "httpd"].each do |name|
76
+ command = find_command(name)
77
+ if !command.nil?
78
+ return command
79
+ end
80
+ end
81
+ return nil
82
+ else
83
+ return find_apache2_executable(`#{apxs2} -q TARGET`.strip, options)
84
+ end
85
+ end
86
+ memoize :httpd
87
+
88
+ # The Apache version, or nil if Apache is not found.
89
+ def self.httpd_version(options = nil)
90
+ if options
91
+ httpd = options[:httpd] || self.httpd(options)
92
+ else
93
+ httpd = self.httpd
94
+ end
95
+ if httpd
96
+ `#{httpd} -v` =~ %r{Apache/([\d\.]+)}
97
+ return $1
98
+ else
99
+ return nil
100
+ end
101
+ end
102
+ memoize :httpd_version
103
+
104
+ # Run `httpd -V` and return its output. On some systems, such as Ubuntu 13.10,
105
+ # `httpd -V` fails without the environment variables defined in various scripts.
106
+ # Here we take care of evaluating those scripts before running `httpd -V`.
107
+ def self.httpd_V(options = nil)
108
+ if options
109
+ httpd = options[:httpd] || self.httpd(options)
110
+ else
111
+ httpd = self.httpd
112
+ end
113
+ if httpd
114
+ command = "#{httpd} -V"
115
+ if envvars_file = httpd_envvars_file(options)
116
+ command = ". '#{envvars_file}' && #{command}"
117
+ end
118
+ return `#{command}`
119
+ else
120
+ return nil
121
+ end
122
+ end
123
+ memoize :httpd_V
124
+
125
+ # The Apache executable's architectural bits. Returns 32 or 64,
126
+ # or nil if unable to detect.
127
+ def self.httpd_architecture_bits(options = nil)
128
+ if options
129
+ httpd = options[:httpd] || self.httpd(options)
130
+ else
131
+ httpd = self.httpd
132
+ end
133
+ if httpd
134
+ `#{httpd} -V` =~ %r{Architecture:(.*)}
135
+ text = $1
136
+ if text =~ /32/
137
+ return 32
138
+ elsif text =~ /64/
139
+ return 64
140
+ else
141
+ return nil
142
+ end
143
+ else
144
+ return nil
145
+ end
146
+ end
147
+ memoize :httpd_architecture_bits
148
+
149
+ # The default Apache root directory, as specified by its compilation parameters.
150
+ # This may be different from the value of the ServerRoot directive.
151
+ def self.httpd_default_root(options = nil)
152
+ if options
153
+ info = httpd_V(options)
154
+ else
155
+ info = httpd_V
156
+ end
157
+ if info
158
+ info =~ / -D HTTPD_ROOT="(.+)"$/
159
+ return $1
160
+ else
161
+ return nil
162
+ end
163
+ end
164
+ memoize :httpd_default_root
165
+
166
+ # The default Apache configuration file, or nil if Apache is not found.
167
+ def self.httpd_default_config_file(options = nil)
168
+ if options
169
+ info = httpd_V(options)
170
+ else
171
+ info = httpd_V
172
+ end
173
+ if info
174
+ info =~ /-D SERVER_CONFIG_FILE="(.+)"$/
175
+ filename = $1
176
+ if filename =~ /\A\//
177
+ return filename
178
+ else
179
+ # Not an absolute path. Infer from default root.
180
+ if root = httpd_default_root(options)
181
+ return "#{root}/#{filename}"
182
+ else
183
+ return nil
184
+ end
185
+ end
186
+ else
187
+ return nil
188
+ end
189
+ end
190
+ memoize :httpd_default_config_file
191
+
192
+ # Given an Apache config file, returns the a hash with the following elements:
193
+ #
194
+ # * `:files` - An array containing `config_file`, as well as all config files
195
+ # included from that config file, including recursively included
196
+ # ones. Only filenames that actually exist are put here.
197
+ # * `:unreadable_files` - All config files that this function was unable
198
+ # to read.
199
+ def self.httpd_included_config_files(config_file, options = nil)
200
+ state = {
201
+ :files => { config_file => true },
202
+ :unreadable_files => [],
203
+ :root => httpd_default_root(options)
204
+ }
205
+ scan_for_included_apache2_config_files(config_file, state, options)
206
+ return {
207
+ :files => state[:files].keys,
208
+ :unreadable_files => state[:unreadable_files]
209
+ }
210
+ end
211
+
212
+ # The default Apache error log's filename, as it is compiled into the Apache
213
+ # main executable. This may not be the actual error log that is used. The actual
214
+ # error log depends on the configuration file.
215
+ #
216
+ # Returns nil if Apache is not detected, or if the default error log filename
217
+ # cannot be detected.
218
+ def self.httpd_default_error_log(options = nil)
219
+ if info = httpd_V(options)
220
+ info =~ /-D DEFAULT_ERRORLOG="(.+)"$/
221
+ filename = $1
222
+ if filename =~ /\A\//
223
+ return filename
224
+ else
225
+ # Not an absolute path. Infer from default root.
226
+ if root = httpd_default_root(options)
227
+ return "#{root}/#{filename}"
228
+ else
229
+ return nil
230
+ end
231
+ end
232
+ else
233
+ return nil
234
+ end
235
+ end
236
+ memoize :httpd_default_error_log
237
+
238
+ def self.httpd_actual_error_log(options = nil)
239
+ if config_file = httpd_default_config_file(options)
240
+ begin
241
+ contents = File.open(config_file, "rb") { |f| f.read }
242
+ rescue Errno::ENOENT
243
+ log "#{config_file} does not exist"
244
+ return nil
245
+ rescue Errno::EACCES
246
+ log "Unable to open #{config_file} for reading"
247
+ return nil
248
+ end
249
+ # We don't want to match comments
250
+ contents.gsub!(/^[ \t]*#.*/, '')
251
+ if contents =~ /^[ \t]*ErrorLog[ \t]+(.+)[ \t]*$/i
252
+ filename = unescape_apache_config_value($1, options)
253
+ if filename && filename !~ /\A\//
254
+ # Not an absolute path. Infer from root.
255
+ if root = httpd_default_root(options)
256
+ return "#{root}/#{filename}"
257
+ else
258
+ return nil
259
+ end
260
+ else
261
+ return filename
262
+ end
263
+ elsif contents =~ /ErrorLog/i
264
+ # The user apparently has ErrorLog set somewhere but
265
+ # we can't parse it. The default error log location,
266
+ # as reported by `httpd -V`, may be wrong (it is on OS X).
267
+ # So to be safe, let's assume that we don't know.
268
+ log "Unable to parse ErrorLog directive in Apache configuration file"
269
+ return nil
270
+ else
271
+ log "No ErrorLog directive in Apache configuration file"
272
+ return httpd_default_error_log(options)
273
+ end
274
+ else
275
+ return nil
276
+ end
277
+ end
278
+ memoize :httpd_actual_error_log
279
+
280
+ # The location of the Apache envvars file, which exists on some systems such as Ubuntu.
281
+ # Returns nil if Apache is not found or if the envvars file is not found.
282
+ def self.httpd_envvars_file(options = nil)
283
+ if options
284
+ httpd = options[:httpd] || self.httpd(options)
285
+ else
286
+ httpd = self.httpd
287
+ end
288
+
289
+ httpd_dir = File.dirname(httpd)
290
+ if httpd_dir == "/usr/bin" || httpd_dir == "/usr/sbin"
291
+ if File.exist?("/etc/apache2/envvars")
292
+ return "/etc/apache2/envvars"
293
+ elsif File.exist?("/etc/httpd/envvars")
294
+ return "/etc/httpd/envvars"
295
+ end
296
+ end
297
+
298
+ conf_dir = File.expand_path(File.dirname(httpd) + "/../conf")
299
+ if File.exist?("#{conf_dir}/envvars")
300
+ return "#{conf_dir}/envvars"
301
+ end
302
+
303
+ return nil
304
+ end
305
+
306
+ def self.httpd_infer_envvar(varname, options = nil)
307
+ if envfile = httpd_envvars_file(options)
308
+ result = `. '#{envfile}' && echo $#{varname}`.strip
309
+ if $? && $?.exitstatus == 0
310
+ return result
311
+ else
312
+ return nil
313
+ end
314
+ else
315
+ return nil
316
+ end
317
+ end
318
+
319
+ # Returns the path to the Apache `mods-available` subdirectory,
320
+ # or nil if it's not supported by this Apache.
321
+ def self.httpd_mods_available_directory(options = nil)
322
+ config_file = httpd_default_config_file(options)
323
+ return nil if !config_file
324
+
325
+ # mods-available is supposed to be a Debian extension that only works
326
+ # on the APT-installed Apache, so only return non-nil if we're
327
+ # working against the APT-installed Apache.
328
+ config_dir = File.dirname(config_file)
329
+ if config_dir == "/etc/httpd" || config_dir == "/etc/apache2"
330
+ if File.exist?("#{config_dir}/mods-available") &&
331
+ File.exist?("#{config_dir}/mods-enabled")
332
+ return "#{config_dir}/mods-available"
333
+ else
334
+ return nil
335
+ end
336
+ else
337
+ return nil
338
+ end
339
+ end
340
+ memoize :httpd_mods_available_directory
341
+
342
+ # Returns the path to the Apache `mods-enabled` subdirectory,
343
+ # or nil if it's not supported by this Apache.
344
+ def self.httpd_mods_enabled_directory(options = nil)
345
+ config_file = httpd_default_config_file(options)
346
+ return nil if !config_file
347
+
348
+ # mods-enabled is supposed to be a Debian extension that only works
349
+ # on the APT-installed Apache, so only return non-nil if we're
350
+ # working against the APT-installed Apache.
351
+ config_dir = File.dirname(config_file)
352
+ if config_dir == "/etc/httpd" || config_dir == "/etc/apache2"
353
+ if File.exist?("#{config_dir}/mods-available") &&
354
+ File.exist?("#{config_dir}/mods-enabled")
355
+ return "#{config_dir}/mods-enabled"
356
+ else
357
+ return nil
358
+ end
359
+ else
360
+ return nil
361
+ end
362
+ end
363
+ memoize :httpd_mods_enabled_directory
364
+
365
+ # The absolute path to the 'a2enmod' executable.
366
+ def self.a2enmod(options = {})
367
+ apxs2 = options[:apxs2] || self.apxs2
368
+ dir = File.dirname(apxs2)
369
+ # a2enmod is supposed to be a Debian extension that only works
370
+ # on the APT-installed Apache, so only return non-nil if we're
371
+ # working against the APT-installed Apache.
372
+ if dir == "/usr/bin" || dir == "/usr/sbin"
373
+ if env_defined?('A2ENMOD')
374
+ return ENV['A2ENMOD']
375
+ else
376
+ return find_apache2_executable("a2enmod", options)
377
+ end
378
+ else
379
+ return nil
380
+ end
381
+ end
382
+ memoize :a2enmod
383
+
384
+ # The absolute path to the 'a2enmod' executable.
385
+ def self.a2dismod(options = {})
386
+ apxs2 = options[:apxs2] || self.apxs2
387
+ dir = File.dirname(apxs2)
388
+ # a2dismod is supposed to be a Debian extension that only works
389
+ # on the APT-installed Apache, so only return non-nil if we're
390
+ # working against the APT-installed Apache.
391
+ if dir == "/usr/bin" || dir == "/usr/sbin"
392
+ if env_defined?('A2DISMOD')
393
+ return ENV['A2DISMOD']
394
+ else
395
+ return find_apache2_executable("a2dismod", options)
396
+ end
397
+ end
398
+ end
399
+ memoize :a2dismod
400
+
401
+ # The absolute path to the 'apr-config' or 'apr-1-config' executable,
402
+ # or nil if not found.
403
+ def self.apr_config
404
+ if env_defined?('APR_CONFIG')
405
+ return ENV['APR_CONFIG']
406
+ elsif apxs2.nil?
407
+ return nil
408
+ else
409
+ filename = `#{apxs2} -q APR_CONFIG 2>/dev/null`.strip
410
+ if filename.empty?
411
+ apr_bindir = `#{apxs2} -q APR_BINDIR 2>/dev/null`.strip
412
+ if apr_bindir.empty?
413
+ return nil
414
+ else
415
+ return select_executable(apr_bindir,
416
+ "apr-1-config", "apr-config")
417
+ end
418
+ elsif File.exist?(filename)
419
+ return filename
420
+ else
421
+ return nil
422
+ end
423
+ end
424
+ end
425
+ memoize :apr_config
426
+
427
+ # The absolute path to the 'apu-config' or 'apu-1-config' executable, or nil
428
+ # if not found.
429
+ def self.apu_config
430
+ if env_defined?('APU_CONFIG')
431
+ return ENV['APU_CONFIG']
432
+ elsif apxs2.nil?
433
+ return nil
434
+ else
435
+ filename = `#{apxs2} -q APU_CONFIG 2>/dev/null`.strip
436
+ if filename.empty?
437
+ apu_bindir = `#{apxs2} -q APU_BINDIR 2>/dev/null`.strip
438
+ if apu_bindir.empty?
439
+ return nil
440
+ else
441
+ return select_executable(apu_bindir,
442
+ "apu-1-config", "apu-config")
443
+ end
444
+ elsif File.exist?(filename)
445
+ return filename
446
+ else
447
+ return nil
448
+ end
449
+ end
450
+ end
451
+ memoize :apu_config
452
+
453
+ # Find an executable in the Apache 'bin' and 'sbin' directories.
454
+ # Returns nil if not found.
455
+ def self.find_apache2_executable(*possible_names)
456
+ if possible_names.last.is_a?(Hash)
457
+ options = possible_names.pop
458
+ options = nil if options.empty?
459
+ end
460
+
461
+ if options
462
+ dirs = options[:dirs] || [apache2_bindir(options), apache2_sbindir(options)]
463
+ else
464
+ dirs = [apache2_bindir, apache2_sbindir]
465
+ end
466
+
467
+ dirs.each do |bindir|
468
+ if bindir.nil?
469
+ next
470
+ end
471
+ possible_names.each do |name|
472
+ filename = "#{bindir}/#{name}"
473
+ if !File.exist?(filename)
474
+ log "Looking for #{filename}: not found"
475
+ elsif !File.file?(filename)
476
+ log "Looking for #{filename}: found, but is not a file"
477
+ elsif !File.executable?(filename)
478
+ log "Looking for #{filename}: found, but is not executable"
479
+ else
480
+ log "Looking for #{filename}: found"
481
+ return filename
482
+ end
483
+ end
484
+ end
485
+ return nil
486
+ end
487
+
488
+
489
+ ################ Directories ################
490
+
491
+ # The absolute path to the Apache 2 'bin' directory, or nil if unknown.
492
+ def self.apache2_bindir(options = {})
493
+ apxs2 = options[:apxs2] || self.apxs2
494
+ if apxs2.nil?
495
+ return nil
496
+ else
497
+ return `#{apxs2} -q BINDIR 2>/dev/null`.strip
498
+ end
499
+ end
500
+ memoize :apache2_bindir
501
+
502
+ # The absolute path to the Apache 2 'sbin' directory, or nil if unknown.
503
+ def self.apache2_sbindir(options = {})
504
+ apxs2 = options[:apxs2] || self.apxs2
505
+ if apxs2.nil?
506
+ return nil
507
+ else
508
+ return `#{apxs2} -q SBINDIR`.strip
509
+ end
510
+ end
511
+ memoize :apache2_sbindir
512
+
513
+
514
+ ################ Compiler and linker flags ################
515
+
516
+ def self.apache2_module_cflags(with_apr_flags = true)
517
+ return apache2_module_c_or_cxxflags(:c, with_apr_flags)
518
+ end
519
+ memoize :apache2_module_cflags, true
520
+
521
+ def self.apache2_module_cxxflags(with_apr_flags = true)
522
+ return apache2_module_c_or_cxxflags(:cxx, with_apr_flags)
523
+ end
524
+ memoize :apache2_module_cxxflags, true
525
+
526
+ # The C compiler flags that are necessary to compile an Apache module.
527
+ # Also includes APR and APU compiler flags if with_apr_flags is true.
528
+ def self.apache2_module_c_or_cxxflags(language, with_apr_flags = true)
529
+ flags = [""]
530
+ if (language == :c && cc_is_sun_studio?) || (language == :cxx && cxx_is_sun_studio?)
531
+ flags << "-KPIC"
532
+ else
533
+ flags << "-fPIC"
534
+ end
535
+ if with_apr_flags
536
+ flags << apr_flags
537
+ flags << apu_flags
538
+ end
539
+ if !apxs2.nil?
540
+ apxs2_flags = `#{apxs2} -q CFLAGS`.strip << " -I" << `#{apxs2} -q INCLUDEDIR`.strip
541
+ apxs2_flags.gsub!(/-O\d? /, '')
542
+
543
+ # Remove flags not supported by GCC
544
+ if os_name =~ /solaris/ # TODO: Add support for people using SunStudio
545
+ # The big problem is Coolstack apxs includes a bunch of solaris -x directives.
546
+ options = apxs2_flags.split
547
+ options.reject! { |f| f =~ /^\-x/ }
548
+ options.reject! { |f| f =~ /^\-Xa/ }
549
+ options.reject! { |f| f =~ /^\-fast/ }
550
+ options.reject! { |f| f =~ /^\-mt/ }
551
+ apxs2_flags = options.join(' ')
552
+ end
553
+
554
+ if os_name == "linux" &&
555
+ linux_distro_tags.include?(:redhat) &&
556
+ apxs2 == "/usr/sbin/apxs" &&
557
+ httpd_architecture_bits == 64
558
+ # The Apache package in CentOS 5 x86_64 is broken.
559
+ # 'apxs -q CFLAGS' contains directives for compiling
560
+ # the module as 32-bit, even though httpd itself
561
+ # is 64-bit. Fix this.
562
+ apxs2_flags.gsub!('-m32 -march=i386 -mtune=generic', '')
563
+ end
564
+
565
+ apxs2_flags.strip!
566
+ flags << apxs2_flags
567
+ end
568
+ if !httpd.nil? && os_name == "macosx"
569
+ # The default Apache install on OS X is a universal binary.
570
+ # Figure out which architectures it's compiled for and do the same
571
+ # thing for mod_passenger. We use the 'file' utility to do this.
572
+ #
573
+ # Running 'file' on the Apache executable usually outputs something
574
+ # like this:
575
+ #
576
+ # /usr/sbin/httpd: Mach-O universal binary with 4 architectures
577
+ # /usr/sbin/httpd (for architecture ppc7400): Mach-O executable ppc
578
+ # /usr/sbin/httpd (for architecture ppc64): Mach-O 64-bit executable ppc64
579
+ # /usr/sbin/httpd (for architecture i386): Mach-O executable i386
580
+ # /usr/sbin/httpd (for architecture x86_64): Mach-O 64-bit executable x86_64
581
+ #
582
+ # But on some machines, it may output just:
583
+ #
584
+ # /usr/sbin/httpd: Mach-O fat file with 4 architectures
585
+ #
586
+ # (http://code.google.com/p/phusion-passenger/issues/detail?id=236)
587
+ output = `file "#{httpd}"`.strip
588
+ if output =~ /Mach-O fat file/ && output !~ /for architecture/
589
+ architectures = ["i386", "ppc", "x86_64", "ppc64"]
590
+ else
591
+ architectures = []
592
+ output.split("\n").grep(/for architecture/).each do |line|
593
+ line =~ /for architecture (.*?)\)/
594
+ architectures << $1
595
+ end
596
+ end
597
+ # The compiler may not support all architectures in the binary.
598
+ # XCode 4 seems to have removed support for the PPC architecture
599
+ # even though there are still plenty of Apache binaries around
600
+ # containing PPC components.
601
+ architectures.reject! do |arch|
602
+ !compiler_supports_architecture?(arch)
603
+ end
604
+ architectures.map! do |arch|
605
+ "-arch #{arch}"
606
+ end
607
+ flags << architectures.compact.join(' ')
608
+ end
609
+ return flags.compact.join(' ').strip
610
+ end
611
+
612
+ # Linker flags that are necessary for linking an Apache module.
613
+ # Already includes APR and APU linker flags.
614
+ def self.apache2_module_cxx_ldflags
615
+ if cxx_is_sun_studio?
616
+ flags = "-KPIC"
617
+ else
618
+ flags = "-fPIC"
619
+ end
620
+ flags << " #{apr_libs} #{apu_libs}"
621
+ flags.strip!
622
+ return flags
623
+ end
624
+ memoize :apache2_module_cxx_ldflags
625
+
626
+ # The C compiler flags that are necessary for programs that use APR.
627
+ def self.apr_flags
628
+ return determine_apr_info[0]
629
+ end
630
+
631
+ # The linker flags that are necessary for linking programs that use APR.
632
+ def self.apr_libs
633
+ return determine_apr_info[1]
634
+ end
635
+
636
+ # The C compiler flags that are necessary for programs that use APR-Util.
637
+ def self.apu_flags
638
+ return determine_apu_info[0]
639
+ end
640
+
641
+ # The linker flags that are necessary for linking programs that use APR-Util.
642
+ def self.apu_libs
643
+ return determine_apu_info[1]
644
+ end
645
+
646
+ ################ Miscellaneous information ################
647
+
648
+
649
+ # Returns whether it is necessary to use information outputted by
650
+ # 'apr-config' and 'apu-config' in order to compile an Apache module.
651
+ # When Apache is installed with --with-included-apr, the APR/APU
652
+ # headers are placed into the same directory as the Apache headers,
653
+ # and so 'apr-config' and 'apu-config' won't be necessary in that case.
654
+ def self.apr_config_needed_for_building_apache_modules?
655
+ return !try_compile("whether APR is needed for building Apache modules",
656
+ :c, "#include <apr.h>\n", apache2_module_cflags(false))
657
+ end
658
+ memoize :apr_config_needed_for_building_apache_modules?, true
659
+
660
+ private
661
+ def self.determine_apr_info
662
+ if apr_config.nil?
663
+ return [nil, nil]
664
+ else
665
+ flags = `#{apr_config} --cppflags --includes`.strip
666
+ libs = `#{apr_config} --link-ld`.strip
667
+ flags.gsub!(/-O\d? /, '')
668
+ if os_name =~ /solaris/
669
+ # Remove flags not supported by GCC
670
+ flags = flags.split(/ +/).reject{ |f| f =~ /^\-mt/ }.join(' ')
671
+ elsif os_name =~ /aix/
672
+ libs << " -Wl,-G -Wl,-brtl"
673
+ end
674
+ return [flags, libs]
675
+ end
676
+ end
677
+ memoize :determine_apr_info, true
678
+ private_class_method :determine_apr_info
679
+
680
+ def self.determine_apu_info
681
+ if apu_config.nil?
682
+ return [nil, nil]
683
+ else
684
+ flags = `#{apu_config} --includes`.strip
685
+ libs = `#{apu_config} --link-ld`.strip
686
+ flags.gsub!(/-O\d? /, '')
687
+ return [flags, libs]
688
+ end
689
+ end
690
+ memoize :determine_apu_info, true
691
+ private_class_method :determine_apu_info
692
+
693
+ def self.scan_for_included_apache2_config_files(config_file, state, options = nil)
694
+ begin
695
+ config = File.open(config_file, "rb") do |f|
696
+ f.read
697
+ end
698
+ rescue Errno::EACCES
699
+ state[:unreadable_files] << config_file
700
+ return
701
+ end
702
+
703
+ found_filenames = []
704
+
705
+ config.scan(/^[ \t]*(Include(Optional)?|ServerRoot)[ \t]+(.+?)[ \t]*$/i) do |match|
706
+ if match[0].downcase == "serverroot"
707
+ new_root = unescape_apache_config_value(match[2], options)
708
+ state[:root] = new_root if new_root
709
+ else
710
+ filename = unescape_apache_config_value(match[2], options)
711
+ next if filename.nil? || filename.empty?
712
+ if filename !~ /\A\//
713
+ # Not an absolute path. Infer from root.
714
+ filename = "#{state[:root]}/#{filename}"
715
+ end
716
+ expand_apache2_glob(filename).each do |filename2|
717
+ if !state[:files].has_key?(filename2)
718
+ state[:files][filename2] = true
719
+ scan_for_included_apache2_config_files(filename2, state, options)
720
+ end
721
+ end
722
+ end
723
+ end
724
+ end
725
+ private_class_method :scan_for_included_apache2_config_files
726
+
727
+ def self.expand_apache2_glob(glob)
728
+ if File.directory?(glob)
729
+ glob = glob.sub(/\/*$/, '')
730
+ result = Dir["#{glob}/**/*"]
731
+ else
732
+ result = []
733
+ Dir[glob].each do |filename|
734
+ if File.directory?(filename)
735
+ result.concat(Dir["#{filename}/**/*"])
736
+ else
737
+ result << filename
738
+ end
739
+ end
740
+ end
741
+ result.reject! do |filename|
742
+ File.directory?(filename)
743
+ end
744
+ return result
745
+ end
746
+ private_class_method :expand_apache2_glob
747
+
748
+ def self.unescape_apache_config_value(value, options = nil)
749
+ if value =~ /^"(.*)"$/
750
+ value = unescape_c_string($1)
751
+ end
752
+ if value.include?("${")
753
+ log "Attempting to substitute environment variables in Apache config value #{value.inspect}..."
754
+ end
755
+ # The Apache config file supports environment variable
756
+ # substitution. Ubuntu uses this extensively.
757
+ value.gsub!(/\$\{(.+?)\}/) do |varname|
758
+ if substitution = httpd_infer_envvar($1, options)
759
+ log "Substituted \"#{varname}\" -> \"#{substitution}\""
760
+ substitution
761
+ else
762
+ log "Cannot substitute \"#{varname}\""
763
+ varname
764
+ end
765
+ end
766
+ if value.include?("${")
767
+ # We couldn't substitute everything.
768
+ return nil
769
+ else
770
+ return value
771
+ end
772
+ end
773
+ private_class_method :unescape_apache_config_value
774
+
775
+ def self.unescape_c_string(s)
776
+ state = 0
777
+ res = ''
778
+ backslash = "\\"
779
+ s.each_char do |c|
780
+ case state
781
+ when 0
782
+ case c
783
+ when backslash then state = 1
784
+ else res << c
785
+ end
786
+ when 1
787
+ case c
788
+ when 'n' then res << "\n"; state = 0
789
+ when 't' then res << "\t"; state = 0
790
+ when backslash then res << backslash; state = 0
791
+ else res << backslash; res << c; state = 0
792
+ end
793
+ end
794
+ end
795
+ return res
796
+ end
797
+ private_class_method :unescape_c_string
798
+ end
799
799
 
800
800
  end