#!/usr/bin/env ruby # Phusion Passenger - http://www.modrails.com/ # Copyright (C) 2008 Phusion # # Phusion Passenger is a trademark of Hongli Lai & Ninh Bui. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. PASSENGER_ROOT = File.expand_path(File.dirname(__FILE__) << "/..") $LOAD_PATH.unshift("#{PASSENGER_ROOT}/lib") $LOAD_PATH.unshift("#{PASSENGER_ROOT}/ext") # The Apache executable may be located in an 'sbin' folder. We add # the 'sbin' folders to $PATH just in case. On some systems # 'sbin' isn't in $PATH unless the user is logged in as root from # the start (i.e. not via 'su' or 'sudo'). ENV["PATH"] += ":/usr/sbin:/sbin:/usr/local/sbin" require 'optparse' require 'phusion_passenger/platform_info' require 'phusion_passenger/dependencies' require 'phusion_passenger/console_text_template' include PlatformInfo class Installer include PhusionPassenger PASSENGER_WEBSITE = "http://www.modrails.com/" PHUSION_WEBSITE = "www.phusion.nl" REQUIRED_DEPENDENCIES = [ Dependencies::GCC, Dependencies::Ruby_DevHeaders, Dependencies::Ruby_OpenSSL, Dependencies::RubyGems, Dependencies::Rake, Dependencies::Apache2, Dependencies::Apache2_DevHeaders ] if Dependencies.fastthread_required? REQUIRED_DEPENDENCIES << Dependencies::FastThread end # Some broken servers don't have apr-config or apu-config installed. # Nevertheless, it is possible to compile Apache modules if Apache # was configured with --included-apr. So here we check whether # apr-config and apu-config are available. If they're not available, # then we only register them as required dependency if no Apache # module can be compiled without their presence. if (PlatformInfo.apr_config && PlatformInfo.apu_config) || PlatformInfo.apr_config_needed_for_building_apache_modules? REQUIRED_DEPENDENCIES << Dependencies::APR_DevHeaders REQUIRED_DEPENDENCIES << Dependencies::APU_DevHeaders end def start(automatic = false) @auto = automatic if natively_packaged? check_dependencies || exit(1) show_apache2_config_snippets show_deployment_example exit end Dir.chdir(PASSENGER_ROOT) show_welcome_screen check_dependencies || exit(1) check_whether_apache_uses_compatible_mpm check_write_permission_to_passenger_root || exit(1) if install_apache2_module show_apache2_config_snippets show_deployment_example else show_possible_solutions_for_compilation_and_installation_problems exit(1) end ensure reset_terminal_colors end private def init_terminal_colors STDOUT.write("\e[0m\e[37m\e[40m") STDOUT.flush end def reset_terminal_colors STDOUT.write("\e[0m") STDOUT.flush end def show_welcome_screen render_template 'welcome', :version => PASSENGER_VERSION wait end def check_dependencies missing_dependencies = [] color_puts "Checking for required software..." puts REQUIRED_DEPENDENCIES.each do |dep| color_print " * #{dep.name}... " result = dep.check if result.found? if result.found_at color_puts "found at #{result.found_at}" else color_puts "found" end else color_puts "not found" missing_dependencies << dep end end if missing_dependencies.empty? return true else puts color_puts "Some required software is not installed." color_puts "But don't worry, this installer will tell you how to install them.\n" color_puts "Press Enter to continue, or Ctrl-C to abort." if natively_packaged? wait(10) else wait end line color_puts "Installation instructions for required software" puts missing_dependencies.each do |dep| print_dependency_installation_instructions(dep) puts end color_puts "If the aforementioned instructions didn't solve your problem, then please take" color_puts "a look at the Users Guide:" puts color_puts " #{USERS_GUIDE}" return false end end def check_whether_apache_uses_compatible_mpm line # 'httpd -V' output is in the form of: # # Server MPM: Prefork # <--- this line is not always available! # ... # Server compiled with.... # -D APACHE_MPM_DIR="server/mpm/prefork" output = `#{PlatformInfo.httpd} -V` output =~ /^Server MPM: +(.*)$/ if $1 mpm = $1.downcase else output =~ /APACHE_MPM_DIR="server\/mpm\/(.*)"/ if $1 mpm = $1.downcase else mpm = nil end end if mpm != "prefork" && mpm != "worker" render_template 'apache_must_be_compiled_with_compatible_mpm', :current_mpm => mpm wait end end def check_write_permission_to_passenger_root File.new("__test__.txt", "w").close return true rescue puts line if Process.uid == 0 render_template 'no_write_permission_to_passenger_root' else render_template 'run_installer_as_root' end return false ensure File.unlink("__test__.txt") rescue nil end def install_apache2_module puts line color_puts 'Compiling and installing Apache 2 module...' puts "cd #{PASSENGER_ROOT}" if ENV['TRACE'] puts "#{RUBY} -S #{RAKE} --trace clean apache2" return system(RUBY, "-S", RAKE, "--trace", "clean", "apache2") else puts "#{RUBY} -S #{RAKE} clean apache2" return system(RUBY, "-S", RAKE, "clean", "apache2") end end def show_apache2_config_snippets puts line if natively_packaged? module_location = "#{PASSENGER_ROOT}/lib/passenger/mod_passenger.so" else module_location = "#{PASSENGER_ROOT}/ext/apache2/mod_passenger.so" end render_template 'apache2_config_snippets', :module_location => module_location, :passenger_root => PASSENGER_ROOT, :ruby => RUBY if natively_packaged? wait(10) else wait end end def show_deployment_example puts line render_template 'deployment_example', :users_guide => USERS_GUIDE, :phusion_website => PHUSION_WEBSITE, :passenger_website => PASSENGER_WEBSITE end def show_possible_solutions_for_compilation_and_installation_problems puts line render_template 'possible_solutions_for_compilation_and_installation_problems', :users_guide => USERS_GUIDE, :passenger_website => PASSENGER_WEBSITE end private def color_print(text) STDOUT.write(ConsoleTextTemplate.new(:text => text).result) STDOUT.flush end def color_puts(text) color_print("#{text}\n") end def render_template(name, options = {}) puts ConsoleTextTemplate.new({ :file => name }, options).result end def line puts "--------------------------------------------" end def wait(timeout = nil) return if @auto begin if timeout require 'timeout' unless defined?(Timeout) begin Timeout.timeout(timeout) do STDIN.readline end rescue Timeout::Error # Do nothing. end else STDIN.readline end rescue Interrupt exit 2 end end def print_dependency_installation_instructions(dep) color_puts " * To install #{dep.name}:" if !dep.install_command.nil? color_puts " Please run #{dep.install_command} as root." elsif !dep.install_instructions.nil? color_puts " " << dep.install_instructions elsif !dep.website.nil? color_puts " Please download it from #{dep.website}" if !dep.website_comments.nil? color_puts " (#{dep.website_comments})" end else color_puts " Search Google." end end def natively_packaged? return self.class.natively_packaged? end def self.natively_packaged? return File.expand_path(File.dirname(__FILE__)) == "/usr/bin" end def self.determine_version if natively_packaged? require 'rbconfig' return File.read("/etc/passenger_version.txt") else File.read("#{PASSENGER_ROOT}/Rakefile") =~ /^PACKAGE_VERSION = "(.*)"$/ return $1 end end def self.determine_users_guide if natively_packaged? return "/usr/share/doc/passenger/Users guide.html" else return "#{PASSENGER_ROOT}/doc/Users guide.html" end end PASSENGER_VERSION = determine_version USERS_GUIDE = determine_users_guide end options = {} parser = OptionParser.new do |opts| opts.banner = "Usage: passenger-install-apache2-module [options]" opts.separator "" opts.separator "Options:" opts.on("-a", "--auto", String, "Automatically build the Apache module,\n" << "#{' ' * 37}without interactively asking for user\n" << "#{' ' * 37}input.") do options[:auto] = true end end begin parser.parse! rescue OptionParser::ParseError => e puts e puts puts "Please see '--help' for valid options." exit 1 end Installer.new.start(options[:auto])