class PhusionPassenger::Standalone::RuntimeInstaller

Installs the Phusion Passenger Standalone runtime by downloading or compiling the Phusion Passenger support binaries and Nginx, and then storing them in the designated directories. This installer is entirely non-interactive.

The following option must be given:

If `targets` contains `:support_binaries`, then you must also specify this options:

If `targets` contains `:nginx`, then you must also specify these options:

Other optional options:

Public Class Methods

new(*args) click to toggle source
Calls superclass method PhusionPassenger::AbstractInstaller.new
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 75
def initialize(*args)
        super(*args)
        raise ArgumentError, "At least one target must be given" if @targets.nil? || @targets.empty?
        if @targets.include?(:support_binaries)
                if PhusionPassenger.natively_packaged?
                        raise ArgumentError, "You cannot specify :support_binaries as a " +
                                "target when natively packaged"
                end
                raise ArgumentError, ":support_dir must be given" if !@support_dir
        end
        if @targets.include?(:nginx)
                raise ArgumentError, ":nginx_dir must be given" if !@nginx_dir
                raise ArgumentError, ":lib_dir must be given" if !@lib_dir
        end
end

Protected Instance Methods

after_install() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 140
def after_install
        super
        FileUtils.remove_entry_secure(@working_dir) if @working_dir
        @plugin.call_hook(:runtime_installer_cleanup) if @plugin
end
before_install() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 131
def before_install
        super
        @plugin.call_hook(:runtime_installer_start, self) if @plugin
        @working_dir = PhusionPassenger::Utils.mktmpdir("passenger.", PlatformInfo.tmpexedir)
        @nginx_version ||= PREFERRED_NGINX_VERSION
        @download_binaries = true if !defined?(@download_binaries)
        @binaries_url_root ||= BINARIES_URL_ROOT
end
dependencies() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 92
def dependencies
        specs = [
                'depcheck_specs/compiler_toolchain',
                'depcheck_specs/ruby',
                'depcheck_specs/gems',
                'depcheck_specs/libs',
                'depcheck_specs/utilities'
        ]
        ids = [
                'gcc',
                'g++',
                'gmake',
                'ruby-openssl',
                'rubygems',
                'rake',
                'rack',
                'libcurl-dev',
                'openssl-dev',
                'zlib-dev',
                'pcre-dev',
                'daemon_controller >= 1.1.0'
        ].compact
        return [specs, ids]
end
run_steps() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 121
def run_steps
        show_welcome_screen if @nginx_dir
        check_whether_os_is_broken
        check_for_download_tool
        download_or_compile_binaries
        puts
        puts "<green><b>All done!</b></green>"
        puts
end
users_guide() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 117
def users_guide
        return "#{PhusionPassenger.doc_dir}/Users guide Standalone.html"
end

Private Instance Methods

begin_progress_bar() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 446
def begin_progress_bar
        if !@begun
                @begun = true
                puts "<banner>Installing Phusion Passenger Standalone...</banner>"
        end
end
check_for_download_tool() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 154
def check_for_download_tool
        puts "<banner>Checking for basic prerequities...</banner>"
        puts

        require 'phusion_passenger/platform_info/depcheck'
        PlatformInfo::Depcheck.load('depcheck_specs/utilities')
        runner = PlatformInfo::Depcheck::ConsoleRunner.new
        runner.add('download-tool')

        result = runner.check_all
        puts
        if !result
                @download_binaries = false
                line
                puts
                render_template 'standalone/download_tool_missing',
                        :runner => runner
                wait
        end
end
check_nginx_binary() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 309
def check_nginx_binary
        puts "Checking whether the downloaded binary is usable..."
        output = %xenv LD_BIND_NOW=1 DYLD_BIND_AT_LAUNCH=1 ./nginx -v 2>&1`
        if $? && $?.exitstatus == 0 && output =~ /nginx version:/
                puts "Nginx binary is usable."
                return true
        else
                @stderr.puts "Nginx binary is not usable."
                return false
        end
end
check_support_binaries() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 261
def check_support_binaries
        ["PassengerWatchdog", "PassengerHelperAgent", "PassengerLoggingAgent"].each do |exe|
                puts "Checking whether the downloaded #{exe} binary is usable..."
                output = %xenv LD_BIND_NOW=1 DYLD_BIND_AT_LAUNCH=1 ./agents/#{exe} --test-binary 1`
                if !$? || $?.exitstatus != 0 || output != "PASS\n"
                        @stderr.puts "Binary #{exe} is not usable."
                        return false
                end
        end
        puts "All support binaries are usable."
        return true
end
check_whether_we_can_write_to(dir) click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 398
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 'standalone/cannot_write_to_dir', :dir => dir
        else
                render_template 'standalone/run_installer_as_root', :dir => dir
        end
        return false
ensure
        File.unlink("#{dir}/__test__.txt") rescue nil
end
compile_nginx(nginx_source_dir) click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 392
def compile_nginx(nginx_source_dir)
        install_nginx_from_source(nginx_source_dir) do |progress, total, status_text|
                show_progress(0.1 + progress / total.to_f * 0.9, 1.0, 1, 1, status_text)
        end
end
compile_support_binaries() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 359
def compile_support_binaries
        begin_progress_bar
        show_progress(0, 1, 1, 1, "Preparing Phusion Passenger...")
        Dir.chdir(PhusionPassenger.source_root) do
                args = "nginx_without_native_support" +
                        " CACHING=false" +
                        " OUTPUT_DIR='#{@support_dir}'"
                begin
                        run_rake_task!(args) do |progress, total|
                                show_progress(progress, total, 1, 1, "Compiling Phusion Passenger...")
                        end
                ensure
                        puts
                end

                system "rm -rf '#{@support_dir}'/agents/{*.o,*.dSYM}"
                system "rm -rf '#{@support_dir}'/common/libboost_oxt"
                system "rm -rf '#{@support_dir}'/*/{*.lo,*.h,*.log,Makefile,libtool,stamp-h1,config.status,.deps}"
                system "rm -rf '#{@support_dir}'/{libeio,libev}/*.o"
                
                # Retain only the object files that are needed for linking the Phusion Passenger module into Nginx.
                nginx_libs = COMMON_LIBRARY.
                        only(*NGINX_LIBS_SELECTOR).
                        set_output_dir("#{@support_dir}/libpassenger_common").
                        link_objects
                Dir["#{@support_dir}/libpassenger_common/**/*"].each do |filename|
                        if !nginx_libs.include?(filename) && File.file?(filename)
                                File.unlink(filename)
                        end
                end
        end
end
copy_files(files, target) { |i| ... } click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 512
def copy_files(files, target)
        FileUtils.mkdir_p(target)
        files.each_with_index do |filename, i|
                next if File.directory?(filename)
                dir = "#{target}/#{File.dirname(filename)}"
                if !File.directory?(dir)
                        FileUtils.mkdir_p(dir)
                end
                FileUtils.install(filename, "#{target}/#{filename}", :mode => File.stat(filename).mode)
                yield(i + 1, files.size)
        end
end
download_and_extract_nginx_sources() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 321
def download_and_extract_nginx_sources
        begin_progress_bar
        puts "Downloading Nginx..."
        if @nginx_tarball
                tarball  = @nginx_tarball
        else
                basename = "nginx-#{@nginx_version}.tar.gz"
                tarball  = "#{@working_dir}/#{basename}"
                if !download("http://nginx.org/download/#{basename}", tarball)
                        puts
                        show_possible_solutions_for_download_and_extraction_problems
                        exit(1)
                end
        end
        nginx_sources_name = "nginx-#{@nginx_version}"
        
        Dir.chdir(@working_dir) do
                begin_progress_bar
                begin
                        result = extract_tarball(tarball) do |progress, total|
                                show_progress(progress / total * 0.1, 1.0, 1, 1, "Extracting Nginx sources...")
                        end
                rescue Exception
                        puts
                        raise
                end
                if result
                        return "#{@working_dir}/#{nginx_sources_name}"
                else
                        puts
                        show_possible_solutions_for_download_and_extraction_problems
                        exit(1)
                end
        end
rescue Interrupt
        exit 2
end
download_nginx_binary() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 274
def download_nginx_binary
        return false if !should_download_binaries?

        puts "<banner>Downloading Nginx binary for your platform, if available...</banner>"
        basename = "nginx-#{@nginx_version}-#{PlatformInfo.cxx_binary_compatibility_id}.tar.gz"
        url      = "#{@binaries_url_root}/#{PhusionPassenger::VERSION_STRING}/#{basename}"
        tarball  = "#{@working_dir}/#{basename}"
        if !download(url, tarball, :cacert => PhusionPassenger.binaries_ca_cert_path, :use_cache => true)
                puts "<b>No binary available for your platform. But don't worry, the " +
                        "necessary binary will be compiled from source instead.</b>"
                puts
                return false
        end

        FileUtils.mkdir_p(@nginx_dir)
        Dir.mkdir("#{@working_dir}/nginx")
        Dir.chdir("#{@working_dir}/nginx") do
                puts "Extracting tarball..."
                result = extract_tarball(tarball)
                return false if !result
                if check_nginx_binary
                        if system("mv '#{@working_dir}/nginx'/* '#{@nginx_dir}'/")
                                return true
                        else
                                @stderr.puts "Error: could not move extracted Nginx binary to the right directory"
                                return false
                        end
                else
                        return false
                end
        end
rescue Interrupt
        exit 2
end
download_or_compile_binaries() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 175
def download_or_compile_binaries
        if should_install_support_binaries?
                support_binaries_downloaded = download_support_binaries
        end
        if should_install_nginx?
                nginx_binary_downloaded = download_nginx_binary
        end
        
        should_compile_support_binaries = should_install_support_binaries? &&
                !support_binaries_downloaded
        should_compile_nginx = should_install_nginx? && !nginx_binary_downloaded

        if should_compile_support_binaries || should_compile_nginx
                if @dont_compile_runtime
                        @stderr.puts "*** ERROR: Refusing to compile the Phusion Passenger Standalone runtime " +
                                "because --no-compile-runtime is given."
                        exit(1)
                end
                check_dependencies(false) || exit(1)
                puts
                if should_compile_support_binaries
                        check_whether_we_can_write_to(@support_dir) || exit(1)
                end
                if should_compile_nginx
                        check_whether_we_can_write_to(@nginx_dir) || exit(1)
                end
        end

        if should_compile_nginx
                nginx_source_dir = download_and_extract_nginx_sources
        end
        if should_compile_support_binaries
                compile_support_binaries
        end
        if should_compile_nginx
                compile_nginx(nginx_source_dir)
        end
end
download_support_binaries() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 229
def download_support_binaries
        return false if !should_download_binaries?

        puts "<banner>Downloading Passenger support binaries for your platform, if available...</banner>"
        basename = "support-#{PlatformInfo.cxx_binary_compatibility_id}.tar.gz"
        url      = "#{@binaries_url_root}/#{PhusionPassenger::VERSION_STRING}/#{basename}"
        tarball  = "#{@working_dir}/#{basename}"
        if !download(url, tarball, :cacert => PhusionPassenger.binaries_ca_cert_path, :use_cache => true)
                puts "<b>No binaries are available for your platform. But don't worry, the " +
                        "necessary binaries will be compiled from source instead.</b>"
                puts
                return false
        end
        
        FileUtils.mkdir_p(@support_dir)
        Dir.mkdir("#{@working_dir}/support")
        Dir.chdir("#{@working_dir}/support") do
                puts "Extracting tarball..."
                return false if !extract_tarball(tarball)
                return false if !check_support_binaries
        end

        if system("mv '#{@working_dir}/support'/* '#{@support_dir}'/")
                return true
        else
                @stderr.puts "Error: could not move extracted files to the support directory"
                return false
        end
rescue Interrupt
        exit 2
end
extract_tarball(filename) { |bytes_read, total_size| ... } click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 459
def extract_tarball(filename)
        File.open(filename, 'rb') do |f|
                IO.popen("tar xzf -", "wb") do |io|
                        buffer = ''
                        buffer = buffer.force_encoding('binary') if buffer.respond_to?(:force_encoding)
                        total_size = File.size(filename)
                        bytes_read = 0
                        yield(bytes_read, total_size) if block_given?
                        begin
                                doing_our_io = true
                                while !f.eof?
                                        f.read(1024 * 8, buffer)
                                        io.write(buffer)
                                        io.flush
                                        bytes_read += buffer.size
                                        doing_our_io = false
                                        yield(bytes_read, total_size) if block_given?
                                        doing_our_io = true
                                end
                        rescue Errno::EPIPE
                                if doing_our_io
                                        return false
                                else
                                        raise
                                end
                        end
                end
                if $?.exitstatus != 0
                        return false
                end
        end
        return true
end
install_nginx_from_source(source_dir) { |0, 1, status_text| ... } click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 554
def install_nginx_from_source(source_dir)
        require 'phusion_passenger/platform_info/compiler'
        Dir.chdir(source_dir) do
                shell = PlatformInfo.find_command('bash') || "sh"
                command = ""
                lib_dir = "#{@lib_dir}/common/libpassenger_common"
                nginx_libs = COMMON_LIBRARY.only(*NGINX_LIBS_SELECTOR).
                        set_output_dir(lib_dir).
                        link_objects_as_string
                command << "env PASSENGER_INCLUDEDIR='#{PhusionPassenger.include_dir}' " <<
                        "PASSENGER_LIBS='#{nginx_libs} #{lib_dir}/../libboost_oxt.a' "
                # RPM thinks it's being smart by scanning binaries for
                # paths and refusing to create package if it detects any
                # hardcoded thats that point to /usr or other important
                # locations. For Phusion Passenger Standalone we do not
                # care at all what the Nginx configured prefix is because
                # we pass it its resource locations during runtime, so
                # work around the problem by configure Nginx with prefix
                # /tmp.
                command << "#{shell} ./configure --prefix=/tmp " <<
                        "#{STANDALONE_NGINX_CONFIGURE_OPTIONS} " <<
                        "'--add-module=#{PhusionPassenger.nginx_module_source_dir}'"
                run_command_with_throbber(command, "Preparing Nginx...") do |status_text|
                        yield(0, 1, status_text)
                end
                
                backlog = ""
                total_lines = %x#{PlatformInfo.gnu_make} --dry-run`.split("\n").size
                IO.popen("#{PlatformInfo.gnu_make} 2>&1", "r") do |io|
                        progress = 1
                        while !io.eof?
                                line = io.readline
                                backlog << line
                                yield(progress, total_lines, "Compiling Nginx core...")
                                progress += 1
                        end
                end
                if $?.exitstatus != 0
                        @stderr.puts
                        @stderr.puts "*** ERROR: unable to compile Nginx."
                        @stderr.puts backlog
                        exit 1
                end
                
                yield(1, 1, 'Copying files...')
                if !system("cp -pR objs/nginx '#{@nginx_dir}/'")
                        @stderr.puts
                        @stderr.puts "*** ERROR: unable to copy Nginx binary."
                        exit 1
                end
                if !strip_binary("#{@nginx_dir}/nginx")
                        @stderr.puts
                        @stderr.puts "*** ERROR: unable to strip debugging symbols from the Nginx binary."
                        exit 1
                end
        end
end
myself() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 442
def myself
        return %xwhoami`.strip
end
rake() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 525
def rake
        return PlatformInfo.rake_command
end
run_command_with_throbber(command, status_text) { |"| ... } click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 493
def run_command_with_throbber(command, status_text)
        backlog = ""
        IO.popen("#{command} 2>&1", "r") do |io|
                throbbers = ['-', '\', '|', '/']
                index = 0
                while !io.eof?
                        backlog << io.readline
                        yield("#{status_text} #{throbbers[index]}")
                        index = (index + 1) % throbbers.size
                end
        end
        if $?.exitstatus != 0
                @stderr.puts
                @stderr.puts backlog
                @stderr.puts "*** ERROR: command failed: #{command}"
                exit 1
        end
end
run_rake_task!(target) { |progress, total_lines| ... } click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 529
def run_rake_task!(target)
        total_lines = %x#{rake} #{target} --dry-run STDERR_TO_STDOUT=1`.split("\n").size - 1
        backlog = ""
        
        IO.popen("#{rake} #{target} --trace STDERR_TO_STDOUT=1", "r") do |io|
                progress = 1
                while !io.eof?
                        line = io.readline
                        if line =~ /^\*\* /
                                yield(progress, total_lines)
                                backlog.replace("")
                                progress += 1
                        else
                                backlog << line
                        end
                end
        end
        if $?.exitstatus != 0
                @stderr.puts
                @stderr.puts "*** ERROR: the following command failed:"
                @stderr.puts(backlog)
                exit 1
        end
end
should_download_binaries?() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 223
def should_download_binaries?
        return PhusionPassenger.installed_from_release_package? &&
                @download_binaries &&
                @binaries_url_root
end
should_install_nginx?() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 219
def should_install_nginx?
        return @targets.include?(:nginx)
end
should_install_support_binaries?() click to toggle source

If this method returns true, then PhusionPassenger.originally_packaged? is also true.

# File lib/phusion_passenger/standalone/runtime_installer.rb, line 215
def should_install_support_binaries?
        return @targets.include?(:support_binaries)
end
show_possible_solutions_for_download_and_extraction_problems() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 453
def show_possible_solutions_for_download_and_extraction_problems
        new_screen
        render_template "standalone/possible_solutions_for_download_and_extraction_problems"
        puts
end
show_progress(progress, total, phase, total_phases, status_text = "") click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 414
def show_progress(progress, total, phase, total_phases, status_text = "")
        if !phase.is_a?(Range)
                phase = phase..phase
        end
        total_progress = (phase.first - 1).to_f / total_phases
        total_progress += (progress.to_f / total) * ((phase.last - phase.first + 1).to_f / total_phases)
        
        max_width = 79
        progress_bar_width = 45
        text = sprintf("[%-#{progress_bar_width}s] %s",
                '*' * (progress_bar_width * total_progress).to_i,
                status_text)
        text = text.ljust(max_width)
        text = text[0 .. max_width - 1]
        if @stdout.tty?
                @stdout.write("#{text}\r")
                @stdout.flush
        else
                if @last_status_text != status_text
                        @last_status_text = status_text
                        @stdout.write("[#{status_text.sub(/\.*$/, '')}]")
                end
                @stdout.write(".")
                @stdout.flush
        end
        @plugin.call_hook(:runtime_installer_progress, total_progress, status_text) if @plugin
end
show_welcome_screen() click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 147
def show_welcome_screen
        render_template 'standalone/welcome',
                :version => @nginx_version,
                :dir => @nginx_dir
        puts
end
strip_binary(filename) click to toggle source
# File lib/phusion_passenger/standalone/runtime_installer.rb, line 612
def strip_binary(filename)
        return system("strip", filename)
end