# Phusion Passenger - https://www.phusionpassenger.com/
# Copyright (c) 2013-2015 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.
PhusionPassenger.require_passenger_lib 'constants'
PhusionPassenger.require_passenger_lib 'platform_info'
PhusionPassenger.require_passenger_lib 'platform_info/ruby'
PhusionPassenger.require_passenger_lib 'platform_info/apache'
PhusionPassenger.require_passenger_lib 'utils/ansi_colors'
require 'pathname'
module PhusionPassenger
module PlatformInfo
# Detects all possible Apache installations on the system, and presents the
# autodetection information to the user in a friendly way. It turns out too
# many people have multiple Apache installations on their system, but they
# don't know about that, or they don't know how to compile against the
# correct Apache installation. This tool helps them.
#
# If you use this class to log things to the terminal, then be sure to set
# the terminal color to Utils::AnsiColors::DEFAULT_TERMINAL_COLOR.
class ApacheDetector
class Result
# These are required and are never nil.
attr_accessor :apxs2, :httpd, :ctl, :version
# These are optional and may be nil.
attr_accessor :a2enmod, :a2dismod
# Ttis may be nil. It depends on whether 'apache2ctl -V' succeeds.
attr_accessor :config_file
# This may be nil. It depends on how well we can infer information from the config file.
attr_accessor :error_log
attr_writer :config_file_broken
def initialize(detector)
@detector = detector
end
def config_file_broken?
@config_file_broken
end
def report
log " * Found Apache #{version}!"
log " Information:"
log " apxs2 : #{apxs2}"
log " Main executable: #{httpd}"
log " Control command: #{ctl}"
log " Config file : #{config_file || 'unknown'}"
log " Error log file : #{error_log || 'unknown'}"
if config_file_broken?
log ""
log " WARNING:"
log " The configuration file seems to be broken! Please double-check it by running:"
log " #{ctl} -t"
end
log ""
log " To install #{PROGRAM_NAME} against this specific Apache version:"
log " #{PlatformInfo.ruby_command} #{PhusionPassenger.bin_dir}/passenger-install-apache2-module --apxs2-path='#{apxs2}'"
log ""
log " To start, stop or restart this specific Apache version:"
log " #{ctl} start"
log " #{ctl} stop"
log " #{ctl} restart"
log ""
if error_log
log " To troubleshoot, please read the logs in this file:"
log " #{error_log}"
log ""
end
end
private
def log(message)
@detector.send(:log, message)
end
end
attr_reader :results
def initialize(output)
@output = output
@results = []
@failures = 0
PlatformInfo.verbose = true
PlatformInfo.log_implementation = lambda do |message|
if message =~ /: found$/
log(" --> #{message}")
else
log(" --> #{message}")
end
end
end
def finish
PlatformInfo.verbose = false
PlatformInfo.log_implementation = nil
end
def detect_all
log "Looking for possible Apache installations..."
apxses = PlatformInfo.find_all_commands("apxs2") +
PlatformInfo.find_all_commands("apxs")
if !apxses.include?(PlatformInfo.apxs2)
PlatformInfo.send(:log, "Looking for #{PlatformInfo.apxs2}: found")
apxses << PlatformInfo.apxs2
end
apxses = remove_symlink_duplications(apxses)
log ""
apxses.each do |apxs2|
detect_one(apxs2)
end
end
def detect_one(apxs2)
log "Analyzing #{apxs2}..."
add_result do |result|
result.apxs2 = apxs2
log "Detecting main Apache executable..."
result.httpd = PlatformInfo.httpd(:apxs2 => apxs2)
if result.httpd
log "Detecting version..."
if result.version = PlatformInfo.httpd_version(:httpd => result.httpd)
log " --> #{result.version}"
else
log " --> Cannot detect version!"
result.httpd = nil
end
end
if result.httpd
log "Detecting control command..."
result.ctl = PlatformInfo.apache2ctl(:apxs2 => apxs2)
result.httpd = nil if !result.ctl
end
if result.httpd
log "Detecting configuration file location..."
result.config_file = PlatformInfo.httpd_default_config_file(:httpd => result.httpd)
if result.config_file
log " --> #{result.config_file}"
else
log " --> Cannot detect default config file location!"
end
end
if result.httpd
log "Detecting error log file..."
result.error_log = PlatformInfo.httpd_actual_error_log(:httpd => result.httpd)
if result.error_log
log " --> #{result.error_log}"
else
log " --> Cannot detect error log file!"
end
end
if result.httpd
log "Detecting a2enmod and a2dismod..."
result.a2enmod = PlatformInfo.a2enmod(:apxs2 => apxs2)
result.a2dismod = PlatformInfo.a2dismod(:apxs2 => apxs2)
end
if result.httpd
result.config_file_broken = PlatformInfo.apache2ctl_V(:apxs2 => apxs2).nil?
end
if result.httpd
log "Found a usable Apache installation using #{apxs2}."
true
else
log "Cannot find a usable Apache installation using #{apxs2}."
false
end
end
log ""
end
def report
if @failures > 0 && Process.uid != 0
user = `whoami`.strip
sudo_s_e = PhusionPassenger::PlatformInfo.ruby_sudo_shell_command("-E")
ruby = PhusionPassenger::PlatformInfo.ruby_command
log ""
log "----------------------------"
log ""
log "Permission problems"
log ""
log "Sorry, this program doesn't have enough permissions to autodetect all your"
log "Apache installations, because it's running as the #{`whoami`.strip} user."
log "Please re-run this program with root privileges:"
log ""
log " export ORIG_PATH=\"$PATH\""
log " #{sudo_s_e}"
log " export PATH=\"$ORIG_PATH\""
log " #{ruby} #{PhusionPassenger.bin_dir}/passenger-config --detect-apache2"
return
end
log "Final autodetection results"
@results.each do |result|
result.report
end
if @results.empty?
PhusionPassenger.require_passenger_lib 'platform_info/depcheck'
PlatformInfo::Depcheck.load("depcheck_specs/apache2")
apache2 = PlatformInfo::Depcheck.find("apache2")
apache2_install_instructions = apache2.install_instructions.split("\n").join("\n ")
# apxs2 is part of the development headers.
apache2_dev = PlatformInfo::Depcheck.find("apache2-dev")
apache2_dev_install_instructions = apache2_dev.install_instructions.split("\n").join("\n ")
log "Sorry, this program cannot find an Apache installation."
log ""
log "Please install Apache and its development headers."
log ""
log " * To install Apache:"
log " #{apache2_install_instructions}"
log ""
log " * To install Apache development headers:"
log " #{apache2_dev_install_instructions}"
log ""
log "If you are sure that you have Apache installed, please read the documentation:"
log "https://www.phusionpassenger.com/library/install/apache/customizing_compilation_process.html#forcing-location-of-command-line-tools-and-dependencies"
elsif @results.size > 1
log "WARNING: You have multiple Apache installations on your system!"
log "You are strongly recommended to read this section of the documentation:"
log "https://www.phusionpassenger.com/install/apache/multiple_apache_installs.html"
end
end
def result_for(apxs2)
# All the results use realpaths, so the input must too.
apxs2 = try_realpath(apxs2)
return @results.find { |r| r.apxs2 == apxs2 }
end
private
def log(message)
if @output.tty?
@output.puts(Utils::AnsiColors.ansi_colorize(message))
else
@output.puts(Utils::AnsiColors.strip_color_tags(message))
end
end
# On Ubuntu, /usr/bin/apxs2 is a symlink to /usr/bin/apxs.
# On recent Arch Linux releases, /bin, /sbin etc are symlinks to
# /usr/bin and /usr/sbin.
# We're only supposed to detect one Apache in that case so we need to
# resolve symlinks.
def remove_symlink_duplications(filenames)
old_size = filenames.size
filenames = filenames.map do |filename|
try_realpath(filename)
end
filenames.uniq!
if old_size != filenames.size
log "#{old_size - filenames.size} symlink duplicate(s) detected; ignoring them."
end
return filenames
end
def add_result
result = Result.new(self)
if yield(result)
@results << result
else
@failures += 1
end
end
def try_realpath(path)
if path
begin
Pathname.new(path).realpath.to_s
rescue Errno::ENOENT, Errno::EACCES
path
end
else
nil
end
end
end
end
end # module Phusion Passenger