#!/usr/bin/env ruby
# Phusion Passenger - https://www.phusionpassenger.com/
# Copyright (c) 2010-2013 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.
source_root = File.expand_path("..", File.dirname(__FILE__))
$LOAD_PATH.unshift("#{source_root}/lib")
require 'phusion_passenger'
PhusionPassenger.locate_directories
require 'optparse'
require 'fileutils'
require 'phusion_passenger/platform_info/ruby'
require 'phusion_passenger/abstract_installer'
class Installer < PhusionPassenger::AbstractInstaller
include PhusionPassenger
def dependencies
specs = [
'depcheck_specs/compiler_toolchain',
'depcheck_specs/ruby',
'depcheck_specs/gems',
'depcheck_specs/libs',
'depcheck_specs/utilities'
]
ids = [
'gcc',
'g++',
'download-tool',
'ruby-dev',
'ruby-openssl',
'rubygems',
'rake',
'rack',
'libcurl-dev',
'openssl-dev',
'zlib-dev'
]
return [specs, ids]
end
def users_guide
return "#{PhusionPassenger.doc_dir}/Users guide Nginx.html"
end
def run_steps
# Make sure the configure script finds the correct
# passenger-config command.
ENV['PATH'] = PhusionPassenger.bin_dir + ":" + ENV['PATH']
if PhusionPassenger.natively_packaged?
not_available_when_natively_packaged
exit(2)
end
show_welcome_screen
check_dependencies || exit(1)
check_whether_we_can_write_to(PhusionPassenger.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)
if PhusionPassenger.originally_packaged?
compile_passenger_support_files || exit(1)
end
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 not_available_when_natively_packaged
render_template 'nginx/not_available_when_natively_packaged'
end
def show_welcome_screen
render_template 'nginx/welcome', :version => VERSION_STRING
wait
end
def compile_passenger_support_files
new_screen
puts "Compiling Passenger support files..."
Dir.chdir(PhusionPassenger.source_root) do
return sh("#{PlatformInfo.rake_command} nginx:clean nginx RELEASE=yes")
end
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
puts "=> Proceeding with choice 1."
return true
elsif @nginx_source_dir
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?
puts "No choice has been given."
false
else
puts "'#{input}' is not a valid choice."
false
end
end
return choice == "1"
end
end
def download_and_extract_pcre
new_screen
puts "PCRE (required by Nginx) not installed, downloading it..."
url = "http://downloads.sourceforge.net/project/pcre/pcre/#{PREFERRED_PCRE_VERSION}/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
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
puts "Downloading Nginx..."
url = "http://www.nginx.org/download/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
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
puts "Where do you want to install Nginx to?"
puts
if @prefix
puts "=> #{@prefix}"
return @prefix
else
prefix = prompt("Please specify a prefix directory [/opt/nginx]") do |input|
if input.empty? || input =~ %r(/)
true
else
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
puts "Where is your Nginx source code located?"
puts
if @nginx_source_dir
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
puts "'#{input}' does not look like an Nginx source directory."
false
end
else
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 = ""
puts "=> No extra configure flags."
else
extra_args = @extra_configure_flags
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
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
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 => PhusionPassenger.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 #{PhusionPassenger.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 => PhusionPassenger.source_root,
:ruby => PlatformInfo.ruby_command
wait
end
def build_nginx_configure_command(prefix, extra_configure_flags = nil)
command = "sh ./configure --prefix='#{prefix}' "
command << "--with-http_ssl_module "
command << "--with-http_gzip_static_module "
command << "--with-http_stub_status_module "
command << "--with-cc-opt='-Wno-error' "
if @pcre_source_dir
command << "--with-pcre='#{@pcre_source_dir}' "
elsif !pcre_is_installed?
command << "--without-http_rewrite_module "
end
command << "--add-module='#{PhusionPassenger.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. Specify 'none' if you\n" <<
"#{' ' * 37}do not want to pass additional flags but do\n" <<
"#{' ' * 37}not want this installer to ask\n" <<
"#{' ' * 37}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).run