#!/usr/bin/env ruby
# Phusion Passenger - http://www.modrails.com/
# Copyright (c) 2010 Phusion
#
# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
passenger_root = File.expand_path("..", File.dirname(__FILE__))
$LOAD_PATH.unshift("#{passenger_root}/lib")
require 'phusion_passenger'
require 'optparse'
require 'fileutils'
require 'phusion_passenger/platform_info/ruby'
require 'phusion_passenger/dependencies'
require 'phusion_passenger/abstract_installer'
class Installer < PhusionPassenger::AbstractInstaller
include PhusionPassenger
include PhusionPassenger::PlatformInfo
def dependencies
result = [
Dependencies::GCC,
Dependencies::Make,
Dependencies::DownloadTool,
Dependencies::Ruby_DevHeaders,
Dependencies::Ruby_OpenSSL,
Dependencies::RubyGems,
Dependencies::Rake,
Dependencies::Rack,
Dependencies::Curl_Dev,
Dependencies::OpenSSL_Dev,
Dependencies::Zlib_Dev
]
if Dependencies.fastthread_required?
result << Dependencies::FastThread
end
return result
end
def users_guide
return "#{DOCDIR}/Users guide Nginx.html"
end
def install!
Dir.chdir(SOURCE_ROOT)
show_welcome_screen
check_dependencies || exit(1)
check_whether_we_can_write_to(SOURCE_ROOT) || exit(1)
download_and_install = should_we_download_and_install_nginx_automatically?
if pcre_is_installed?
@pcre_source_dir = nil
else
@pcre_source_dir = download_and_extract_pcre
end
if download_and_install
nginx_source_dir = download_and_extract_nginx
if nginx_source_dir.nil?
show_possible_solutions_for_download_and_extraction_problems
exit(1)
end
nginx_prefix = ask_for_nginx_install_prefix
if @extra_configure_flags == "none"
extra_nginx_configure_flags = nil
else
extra_nginx_configure_flags = @extra_configure_flags
end
else
nginx_source_dir = ask_for_nginx_source_dir
nginx_prefix = ask_for_nginx_install_prefix
extra_nginx_configure_flags = ask_for_extra_nginx_configure_flags(nginx_prefix)
end
check_whether_we_can_write_to(nginx_prefix) || exit(1)
nginx_config_already_exists_before_installing = nginx_config_exists?(nginx_prefix)
compile_passenger_support_files || exit(1)
if install_nginx(nginx_source_dir, nginx_prefix, extra_nginx_configure_flags)
if nginx_config_already_exists_before_installing || !locate_nginx_config_file(nginx_prefix)
show_passenger_config_snippets(nginx_prefix)
else
insert_passenger_config_snippets(nginx_prefix)
end
show_deployment_example
else
show_possible_solutions_for_compilation_and_installation_problems
exit(1)
end
end
def before_install
super
myself = `whoami`.strip
@working_dir = "/tmp/#{myself}-passenger-#{Process.pid}"
FileUtils.rm_rf(@working_dir)
FileUtils.mkdir_p(@working_dir)
end
def after_install
super
FileUtils.rm_rf(@working_dir)
end
private
def show_welcome_screen
render_template 'nginx/welcome', :version => VERSION_STRING
wait
end
def compile_passenger_support_files
new_screen
color_puts "Compiling Passenger support files..."
return sh("#{PlatformInfo.rake_command} nginx:clean nginx RELEASE=yes")
end
def should_we_download_and_install_nginx_automatically?
new_screen
render_template 'nginx/query_download_and_install',
:nginx_version => PREFERRED_NGINX_VERSION
puts
if @auto_download
color_puts "=> Proceeding with choice 1."
return true
elsif @nginx_source_dir
color_puts "=> Proceeding with choice 2."
return false
else
choice = prompt("Enter your choice (1 or 2) or press Ctrl-C to abort") do |input|
if input == "1" || input == "2"
true
elsif input.empty?
color_puts "No choice has been given."
false
else
color_puts "'#{input}' is not a valid choice."
false
end
end
return choice == "1"
end
end
def download_and_extract_pcre
new_screen
color_puts "PCRE (required by Nginx) not installed, downloading it..."
url = "ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-#{PREFERRED_PCRE_VERSION}.tar.gz"
dirname = "pcre-#{PREFERRED_PCRE_VERSION}"
tarball = "#{@working_dir}/pcre.tar.gz"
if download(url, tarball)
Dir.chdir(@working_dir) do
color_puts "Extracting PCRE source tarball..."
if sh("tar", "xzvf", tarball)
return "#{@working_dir}/#{dirname}"
else
new_screen
render_template "nginx/pcre_could_not_be_extracted"
wait
return nil
end
end
else
new_screen
render_template "nginx/pcre_could_not_be_downloaded"
wait
return nil
end
rescue Interrupt
exit 2
end
def download_and_extract_nginx
new_screen
color_puts "Downloading Nginx..."
url = "http://sysoev.ru/nginx/nginx-#{PREFERRED_NGINX_VERSION}.tar.gz"
dirname = "nginx-#{PREFERRED_NGINX_VERSION}"
tarball = "#{@working_dir}/nginx.tar.gz"
if download(url, tarball)
Dir.chdir(@working_dir) do
color_puts "Extracting Nginx source tarball..."
if sh("tar", "xzvf", tarball)
return "#{@working_dir}/#{dirname}"
else
return nil
end
end
else
return nil
end
rescue Interrupt
exit 2
end
def show_possible_solutions_for_download_and_extraction_problems
new_screen
render_template "nginx/possible_solutions_for_download_and_extraction_problems"
puts
end
def ask_for_nginx_install_prefix
new_screen
color_puts "Where do you want to install Nginx to?"
puts
if @prefix
color_puts "=> #{@prefix}"
return @prefix
else
prefix = prompt("Please specify a prefix directory [/opt/nginx]") do |input|
if input.empty? || input =~ %r(/)
true
else
color_puts "Please specify an absolute path."
false
end
end
if prefix.empty?
prefix = "/opt/nginx"
end
return prefix
end
end
def ask_for_nginx_source_dir
new_screen
color_puts "Where is your Nginx source code located?"
puts
if @nginx_source_dir
color_puts "=> #{@nginx_source_dir}"
return @nginx_source_dir
else
return prompt("Please specify the directory") do |input|
if input =~ %r(/)
if File.exist?("#{input}/src/core/nginx.c")
true
else
color_puts "'#{input}' does not look like an Nginx source directory."
false
end
else
color_puts "Please specify an absolute path."
false
end
end
end
end
def ask_for_extra_nginx_configure_flags(prefix)
done = false
while !done
new_screen
render_template 'nginx/ask_for_extra_configure_flags',
:command => build_nginx_configure_command(prefix)
puts
if @extra_configure_flags
if @extra_configure_flags == "none"
extra_args = ""
color_puts "=> No extra configure flags."
else
extra_args = @extra_configure_flags
color_puts "=> #{extra_args}"
end
return extra_args
else
extra_args = prompt "Extra arguments to pass to configure script"
new_screen
render_template 'nginx/confirm_extra_configure_flags',
:command => build_nginx_configure_command(prefix, extra_args)
puts
answer = prompt("Is this what you want? (yes/no) [default=yes]") do |input|
if input.empty? || input == "yes" || input == "no"
true
else
color_puts "Please enter 'yes' or 'no'."
false
end
end
done = answer.empty? || answer == "yes"
end
end
return extra_args
end
def check_whether_we_can_write_to(dir)
FileUtils.mkdir_p(dir)
File.new("#{dir}/__test__.txt", "w").close
return true
rescue
new_screen
if Process.uid == 0
render_template 'nginx/cannot_write_to_dir', :dir => dir
else
render_template 'nginx/run_installer_as_root', :dir => dir
end
return false
ensure
File.unlink("#{dir}/__test__.txt") rescue nil
end
def nginx_config_exists?(prefix)
return !!locate_nginx_config_file(prefix)
end
def install_nginx(source_dir, prefix, extra_configure_flags)
Dir.chdir(source_dir) do
new_screen
color_puts "Compiling and installing Nginx..."
if !sh(build_nginx_configure_command(prefix, extra_configure_flags)) ||
!sh("make") ||
!sh("make install")
return false
end
end
return true
end
def show_passenger_config_snippets(prefix)
new_screen
render_template 'nginx/config_snippets',
:config_file => locate_nginx_config_file(prefix),
:passenger_root => SOURCE_ROOT,
:ruby => PlatformInfo.ruby_command
wait
end
def show_deployment_example
line
puts
render_template 'nginx/deployment_example',
:users_guide => users_guide,
:phusion_website => PHUSION_WEBSITE,
:passenger_website => PASSENGER_WEBSITE
end
def show_possible_solutions_for_compilation_and_installation_problems
line
puts
render_template 'nginx/possible_solutions_for_compilation_and_installation_problems',
:users_guide => users_guide,
:passenger_website => PASSENGER_WEBSITE
end
def locate_nginx_config_file(prefix)
["#{prefix}/conf/nginx.conf", "#{prefix}/etc/nginx.conf"].each do |filename|
if File.exist?(filename)
return filename
end
end
return nil
end
def insert_passenger_config_snippets(prefix)
config_file = locate_nginx_config_file(prefix)
contents = File.read(config_file)
contents.sub!(/^http \{/,
"http {\n" <<
" passenger_root #{SOURCE_ROOT};\n" <<
" passenger_ruby #{PlatformInfo.ruby_command};\n")
File.open(config_file, 'w') do |f|
f.write(contents)
end
new_screen
render_template 'nginx/config_snippets_inserted',
:config_file => config_file,
:passenger_root => SOURCE_ROOT,
:ruby => PlatformInfo.ruby_command
wait
end
def build_nginx_configure_command(prefix, extra_configure_flags = nil)
command = "sh ./configure --prefix='#{prefix}' --with-http_ssl_module "
if @pcre_source_dir
command << "--with-pcre='#{@pcre_source_dir}' "
elsif !pcre_is_installed?
command << "--without-http_rewrite_module "
end
command << "--add-module='#{SOURCE_ROOT}/ext/nginx' #{extra_configure_flags}"
command.strip!
return command
end
def pcre_is_installed?
if @pcre_is_installed.nil?
@pcre_is_installed = begin
File.open('/tmp/passenger-check.c', 'w') do |f|
f.puts("#include ")
end
Dir.chdir('/tmp') do
# Nginx checks for PCRE in multiple places...
system("(gcc -I/usr/local/include -I/usr/include/pcre " <<
"-I/usr/pkg/include -I/opt/local/include " <<
"-c passenger-check.c) >/dev/null 2>/dev/null")
end
ensure
File.unlink('/tmp/passenger-check.c') rescue nil
File.unlink('/tmp/passenger-check.o') rescue nil
end
end
return @pcre_is_installed
end
end
options = {}
parser = OptionParser.new do |opts|
opts.banner = "Usage: passenger-install-nginx-module [options]"
opts.separator ""
opts.separator "Options:"
opts.on("--auto", "Automatically confirm 'Press ENTER to\n" <<
"#{' ' * 37}continue' prompts.") do
options[:auto] = true
end
opts.on("--prefix=DIR", String, "Use the given Nginx install prefix instead\n" <<
"#{' ' * 37}of asking for it interactively.") do |dir|
options[:prefix] = dir
end
opts.on("--auto-download", "Download and install Nginx automatically,\n" <<
"#{' ' * 37}instead of asking interactively whether to\n" <<
"#{' ' * 37}download+install or to use an existing\n" <<
"#{' ' * 37}Nginx source directory.") do
options[:auto_download] = true
end
opts.on("--nginx-source-dir=DIR", String, "Compile and install Nginx using the given\n" <<
"#{' ' * 37}Nginx source directory, instead of\n" <<
"#{' ' * 37}interactively asking to download+install\n" <<
"#{' ' * 37}or to use an existing Nginx source\n" <<
"#{' ' * 37}directory. Conflicts with --auto-download.") do |dir|
options[:nginx_source_dir] = dir
end
opts.on("--extra-configure-flags=STRING", String, "Pass these extra flags to Nginx's\n" <<
"#{' ' * 37}'configure' script, instead of asking for\n" <<
"#{' ' * 37}it interactively. Only applicable if\n" <<
"#{' ' * 37}--nginx-source-dir is given. Specify\n" <<
"#{' ' * 37}'none' if you do not want to pass\n" <<
"#{' ' * 37}additional flags but do not want this\n" <<
"#{' ' * 37}installer to ask interactively either.") do |flags|
options[:extra_configure_flags] = flags
end
end
begin
parser.parse!
rescue OptionParser::ParseError => e
puts e
puts
puts "Please see '--help' for valid options."
exit 1
end
if options[:auto_download] && options[:nginx_source_dir]
STDERR.puts "You cannot specify both --auto-download and --nginx-source-dir."
exit 1
end
Installer.new(options).start