#!/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