#!/usr/bin/env ruby # Phusion Passenger - http://www.modrails.com/ # Copyright (c) 2009 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") $LOAD_PATH.unshift("#{PASSENGER_ROOT}/ext") require 'optparse' require 'fileutils' require 'phusion_passenger/platform_info' require 'phusion_passenger/dependencies' require 'phusion_passenger/abstract_installer' include PlatformInfo class Installer < PhusionPassenger::AbstractInstaller include PhusionPassenger NGINX_VERSION = "0.6.37" def dependencies result = [ Dependencies::GCC, Dependencies::Ruby_DevHeaders, Dependencies::Ruby_OpenSSL, Dependencies::RubyGems, Dependencies::Rake, 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(PASSENGER_ROOT) show_welcome_screen check_dependencies || exit(1) check_whether_we_can_write_to(PASSENGER_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 extra_nginx_configure_flags = nil 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 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(RUBY, "-S", PlatformInfo.rake, "clean", "nginx") end def should_we_download_and_install_nginx_automatically? new_screen render_template 'nginx/query_download_and_install', :nginx_version => 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..." basename = "pcre-7.8.tar.gz" dirname = "pcre-7.8" url = "ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/#{basename}" File.unlink("/tmp/#{basename}") rescue nil FileUtils.rm_rf("/tmp/#{dirname}") if download(url, "/tmp/#{basename}") Dir.chdir("/tmp") do color_puts "Extracting PCRE source tarball..." if sh("tar", "xzvf", basename) return "/tmp/#{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..." basename = "nginx-#{NGINX_VERSION}.tar.gz" url = "http://sysoev.ru/nginx/#{basename}" File.unlink("/tmp/#{basename}") rescue nil FileUtils.rm_rf("/tmp/nginx-#{NGINX_VERSION}") if download(url, "/tmp/#{basename}") Dir.chdir("/tmp") do color_puts "Extracting Nginx source tarball..." if sh("tar", "xzvf", basename) return "/tmp/nginx-#{NGINX_VERSION}" 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 => PASSENGER_ROOT, :ruby => RUBY 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 #{PASSENGER_ROOT};\n" << " passenger_ruby #{RUBY};\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 => PASSENGER_ROOT, :ruby => RUBY wait end def build_nginx_configure_command(prefix, extra_configure_flags = nil) command = "./configure --prefix='#{prefix}' " if @pcre_source_dir command << "--with-pcre='#{@pcre_source_dir}' " elsif !pcre_is_installed? command << "--without-http_rewrite_module " end command << "--add-module='#{PASSENGER_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 system("(gcc -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 def download(url, output) if PlatformInfo.find_command("wget") return sh("wget", "-O", output, url) else return sh("curl", url, "-o", output) end 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