module PhusionPassenger::Config::InstallationUtils

def self.included(klass)

def self.included(klass)
  # When included into another class, make sure that Utils
  # methods are made private.
  public_instance_methods(false).each do |method_name|
    klass.send(:private, method_name)
  end
end

def check_for_download_tool!

def check_for_download_tool!
  PlatformInfo::Depcheck.load('depcheck_specs/utilities')
  result = PlatformInfo::Depcheck.find('download-tool').check
  # Don't output anything if there is a download tool.
  # We want to be as quiet as possible.
  return if result && result[:found]
  colors = @colors || Utils::AnsiColors.new
  puts colors.ansi_colorize("<banner>Checking for basic prerequities...</banner>")
  puts
  runner = PlatformInfo::Depcheck::ConsoleRunner.new
  runner.add('download-tool')
  result = runner.check_all
  puts
  if !result
    puts "---------------------------------------"
    puts
    render_template 'installation_utils/download_tool_missing',
      :runner => runner
    abort
  end
end

def create_user_support_binaries_dir!

def create_user_support_binaries_dir!
  dir = PhusionPassenger.user_support_binaries_dir
  begin
    mkdir_p_preserve_parent_owner(dir)
  rescue Errno::EACCES
    print_installation_error_header
    render_template 'installation_utils/cannot_create_user_support_binaries_dir',
      :dir => dir,
      :myself => myself
    abort
  rescue SystemCallError
    print_installation_error_header
    render_template 'installation_utils/unexpected_filesystem_problem',
      :dir => dir,
      :exception => result
    abort
  end
end

def directory_writable?(path)

checks.
don't always work right with ACLs. Instead of we use 'real'
We can't use File.writable() and friends here because they
def directory_writable?(path)
  filename = "#{path}/.__test_#{object_id}__.txt"
  @logger.debug "Checking whether we can write to #{path}..." if @logger
  begin
    File.new(filename, "w").close
    @logger.debug "Yes" if @logger
    return true
  rescue Errno::EACCES
    @logger.debug "No" if @logger
    return false
  rescue SystemCallError => e
    @logger.warn "Unable to check whether we can write to #{path}: #{e}" if @logger
    return e
  ensure
    File.unlink(filename) rescue nil
  end
end

def find_or_create_writable_support_binaries_dir!

def find_or_create_writable_support_binaries_dir!
  if File.exist?(PhusionPassenger.support_binaries_dir)
    result = directory_writable?(PhusionPassenger.support_binaries_dir)
    if result == true  # return value can be a SystemCallError
      return PhusionPassenger.support_binaries_dir
    end
    if Process.euid == 0
      if result == false
        print_installation_error_header
        render_template 'installation_utils/support_binaries_dir_not_writable_despite_running_as_root',
          :dir => PhusionPassenger.support_binaries_dir,
          :myself => myself
      else
        render_template 'installation_utils/unexpected_filesystem_problem',
          :dir => PhusionPassenger.support_binaries_dir,
          :exception => result
      end
      abort
    else
      return find_or_create_writable_user_support_binaries_dir!
    end
  else
    if Process.euid == 0
      mkdir_p_preserve_parent_owner(PhusionPassenger.support_binaries_dir)
      return PhusionPassenger.support_binaries_dir
    else
      return find_or_create_writable_user_support_binaries_dir!
    end
  end
end

def find_or_create_writable_user_support_binaries_dir!

def find_or_create_writable_user_support_binaries_dir!
  if !File.exist?(PhusionPassenger.user_support_binaries_dir)
    create_user_support_binaries_dir!
  end
  result = directory_writable?(PhusionPassenger.user_support_binaries_dir)
  case result
  when true
    return PhusionPassenger.user_support_binaries_dir
  when false
    print_installation_error_header
    render_template 'installation_utils/user_support_binaries_dir_not_writable'
    abort
  else
    print_installation_error_header
    render_template 'installation_utils/unexpected_filesystem_problem',
      :dir => PhusionPassenger.support_binaries_dir,
      :exception => result
    abort
  end
end

def mkdir_p_preserve_parent_owner(path)

won't mess up permissions.
with sudo privileged, even though Phusion Passenger isn't installed as root,
parent directory's UID and GID. This way, running `passenger-config compile-agent`
When creating PhusionPassenger.support_binaries_dir, preserve the
def mkdir_p_preserve_parent_owner(path)
  Pathname.new(path).descend do |subpath|
    if !subpath.exist?
      stat = subpath.parent.stat
      Dir.mkdir(subpath.to_s)
      if Process.euid == 0
        File.chown(stat.uid, stat.gid, subpath.to_s)
      end
    end
  end
end

def myself

def myself
  return `whoami`.strip
end

def print_installation_error_header

Override this method to print a different header
def print_installation_error_header
  if @colors
    red = @colors.red
    reset = @colors.reset
  else
    red = nil
    reset = nil
  end
  @logger.warn "------------------------------------------" if @logger
  puts "#{red}Cannot proceed with installation#{reset}"
  puts
end

def rake

def rake
  return "env NOEXEC_DISABLE=1 #{PlatformInfo.rake_command}"
end

def render_template(name, options = {})

def render_template(name, options = {})
  options.merge!(:colors => @colors || PhusionPassenger::Utils::AnsiColors.new)
  puts ConsoleTextTemplate.new({ :file => "config/#{name}" }, options).result
end

def run_rake_task!(target)

def run_rake_task!(target)
  total_lines = `#{rake} #{target} --dry-run STDERR_TO_STDOUT=1`.split("\n").size - 1
  backlog = ""
  command = "#{rake} #{target} --trace STDERR_TO_STDOUT=1"
  IO.popen(command, "rb") do |io|
    progress = 1
    while !io.eof?
      line = io.readline
      yield(progress, total_lines)
      if line =~ /^\*\* /
        backlog.replace("")
        progress += 1
      else
        backlog << line
      end
    end
  end
  if $?.exitstatus != 0
    stderr = @stderr || STDERR
    stderr.puts
    stderr.puts "*** ERROR: the following command failed:"
    stderr.puts(backlog)
    exit 1
  end
end