Current File : //opt/cpanel/ea-ruby27/root/sbin/passenger-status |
#!/opt/cpanel/ea-ruby27/root/usr/bin/ruby
# Phusion Passenger - https://www.phusionpassenger.com/
# Copyright (c) 2010-2017 Phusion Holding B.V.
#
# "Passenger", "Phusion Passenger" and "Union Station" are registered
# trademarks of Phusion Holding B.V.
#
# 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.
ENV["PASSENGER_LOCATION_CONFIGURATION_FILE"] = "/opt/cpanel/ea-ruby27/root/usr/share/passenger/phusion_passenger/locations.ini"
begin
require 'rubygems'
rescue LoadError
end
require '/opt/cpanel/ea-ruby27/root/usr/share/passenger/phusion_passenger'
PhusionPassenger.locate_directories
PhusionPassenger.require_passenger_lib 'constants'
PhusionPassenger.require_passenger_lib 'platform_info'
PhusionPassenger.require_passenger_lib 'admin_tools/instance_registry'
PhusionPassenger.require_passenger_lib 'config/utils'
PhusionPassenger.require_passenger_lib 'utils/ansi_colors'
require 'optparse'
require 'socket'
require 'json'
require 'net/http'
include PhusionPassenger::SharedConstants
include PhusionPassenger::AdminTools
include PhusionPassenger::Utils::AnsiColors
DEFAULT_OPTIONS = { :show => 'pool', :color => STDOUT.tty? }.freeze
##### Show status command #####
def command_show_status(argv, options)
if argv.empty?
instance = find_sole_instance
elsif options[:pid_identifier]
instance = find_instance_by_watchdog_pid(argv[0].to_i)
else
instance = find_instance_by_name_prefix(argv[0])
end
show_status(instance, options)
end
def find_sole_instance
instances = InstanceRegistry.new.list
if instances.empty?
abort "ERROR: #{PROGRAM_NAME} doesn't seem to be running. If you " +
"are sure that it is running, then the causes of this problem could be:\n\n" +
"1. You customized the instance registry directory using Apache's " +
"PassengerInstanceRegistryDir option, Nginx's passenger_instance_registry_dir " +
"option, or #{PROGRAM_NAME} Standalone's --instance-registry-dir command line " +
"argument. If so, please set the environment variable PASSENGER_INSTANCE_REGISTRY_DIR " +
"to that directory and run passenger-status again.\n" +
"2. The instance directory has been removed by an operating system background service. " +
"Please set a different instance registry directory using Apache's " +
"PassengerInstanceRegistryDir option, Nginx's passenger_instance_registry_dir " +
"option, or #{PROGRAM_NAME} Standalone's --instance-registry-dir command line " +
"argument."
elsif instances.size == 1
return instances.first
else
puts "It appears that multiple #{PROGRAM_NAME} instances are running. Please"
puts "select a specific one by running:"
puts
puts " passenger-status <NAME>"
puts
PhusionPassenger::Config::Utils.list_all_passenger_instances(instances)
exit 1
end
end
def find_instance_by_name_prefix(name)
instance = InstanceRegistry.new.find_by_name_prefix(name)
if instance == :ambigious
abort "ERROR: there are multiple instances whose name start with '#{name}'. Please specify the full name."
elsif instance
return instance
else
abort "ERROR: there doesn't seem to be a #{PROGRAM_NAME} instance running with the name '#{name}'."
end
end
def find_instance_by_watchdog_pid(pid)
instance = InstanceRegistry.new.find_by_watchdog_pid(pid)
if instance
return instance
else
abort "ERROR: there doesn't seem to be a #{PROGRAM_NAME} instance running with the pid #{pid}."
end
end
def show_status(instance, options)
# if the noshow override is not specified, the default is to show the header, unless show=xml
if options[:noheader] != true && !['xml','json'].include?(options[:show])
print_header(STDOUT, instance)
end
case options[:show]
when 'pool'
request = Net::HTTP::Get.new("/pool.txt?colorize=#{options[:color]}&verbose=#{options[:verbose]}")
try_performing_ro_admin_basic_auth(request, instance)
response = instance.http_request("agents.s/core_api", request)
if response.code.to_i / 100 == 2
puts response.body
elsif response.code.to_i == 401
if response["pool-empty"] == "true"
puts "#{PROGRAM_NAME} is currently not serving any applications."
else
print_permission_error_message
exit 2
end
else
STDERR.puts "*** An error occured."
STDERR.puts "#{response.code}: #{response.body}"
exit 2
end
when 'requests', 'server'
request = Net::HTTP::Get.new("/server.json")
try_performing_ro_admin_basic_auth(request, instance)
response = instance.http_request("agents.s/core_api", request)
if response.code.to_i / 100 == 2
puts response.body
elsif response.code.to_i == 401
print_permission_error_message
exit 2
else
STDERR.puts "*** An error occured."
STDERR.puts "#{response.code}: #{response.body}"
exit 2
end
when 'backtraces'
request = Net::HTTP::Get.new("/backtraces.txt")
try_performing_ro_admin_basic_auth(request, instance)
response = instance.http_request("agents.s/core_api", request)
if response.code.to_i / 100 == 2
text = response.body
# Colorize output
color = PhusionPassenger::Utils::AnsiColors.new
text.gsub!(/^(Thread .*:)$/, color.black_bg + color.yellow + '\1' + color.reset)
text.gsub!(/^( +in '.*? )(.*?)\(/, '\1' + color.bold + '\2' + color.reset + '(')
puts text
elsif response.code.to_i == 401
print_permission_error_message
exit 2
else
STDERR.puts "*** An error occured."
STDERR.puts "#{response.code}: #{response.body}"
exit 2
end
when 'xml'
request = Net::HTTP::Get.new("/pool.xml?secrets=#{options[:verbose]}")
try_performing_ro_admin_basic_auth(request, instance)
response = instance.http_request("agents.s/core_api", request)
if response.code.to_i / 100 == 2
indented = format_with_xmllint(response.body)
if indented
puts indented
else
puts response.body
STDERR.puts "*** Tip: if you install the 'xmllint' command then the XML output will be indented."
end
elsif response.code.to_i == 401
if response["pool-empty"] == "true"
puts "#{PROGRAM_NAME} is currently not serving any applications."
else
print_permission_error_message
exit 2
end
else
STDERR.puts "*** An error occured."
STDERR.puts "#{response.code}: #{response.body}"
exit 2
end
when 'json'
request = Net::HTTP::Get.new("/pool.json?secrets=#{options[:verbose]}")
try_performing_ro_admin_basic_auth(request, instance)
response = instance.http_request("agents.s/core_api", request)
if response.code.to_i / 100 == 2
indented = JSON.pretty_generate(JSON.parse(response.body))
if indented
puts indented
else
puts response.body
end
elsif response.code.to_i == 401
if response["pool-empty"] == "true"
puts "#{PROGRAM_NAME} is currently not serving any applications."
else
print_permission_error_message
exit 2
end
else
STDERR.puts "*** An error occured."
STDERR.puts "#{response.code}: #{response.body}"
exit 2
end
when 'union_station'
request = Net::HTTP::Get.new("/server.json")
try_performing_ro_admin_basic_auth(request, instance)
response = instance.http_request("agents.s/ust_router_api", request)
if response.code.to_i / 100 == 2
puts response.body
elsif response.code.to_i == 401
print_permission_error_message
exit 2
else
STDERR.puts "*** An error occured."
STDERR.puts "#{response.code}: #{response.body}"
exit 2
end
end
end
def print_header(io, instance)
io.puts "Version : #{PhusionPassenger::VERSION_STRING}"
io.puts "Date : #{Time.now}"
io.puts "Instance: #{instance.name} (#{instance.server_software})"
io.puts
end
def try_performing_ro_admin_basic_auth(request, instance)
begin
password = instance.read_only_admin_password
rescue Errno::EACCES
return
end
request.basic_auth("ro_admin", password)
end
def print_permission_error_message
PhusionPassenger.require_passenger_lib 'platform_info/ruby'
STDERR.puts "*** ERROR: You are not authorized to query the status for this " <<
"#{PROGRAM_NAME} instance. Please try again with '#{PhusionPassenger::PlatformInfo.ruby_sudo_command}'."
end
def format_with_xmllint(xml)
return nil if !PhusionPassenger::PlatformInfo.find_command('xmllint')
require 'open3'
require 'thread'
ENV['XMLLINT_INDENT'] = ' '
Open3.popen3("xmllint", "--format", "-") do |stdin, stdout, stderr|
stdout_text = nil
stderr_text = nil
thread1 = Thread.new do
stdin.write(xml)
stdin.close
end
thread2 = Thread.new do
stdout_text = stdout.read
stdout.close
end
thread3 = Thread.new do
stderr_text = stderr.read
stderr.close
end
thread1.join
thread2.join
thread3.join
if stdout_text.nil? || stdout_text.empty?
if stderr_text !~ /No such file or directory/ && stderr_text !~ /command not found/
STDERR.puts stderr_text
end
return nil
else
return stdout_text
end
end
end
##### Main command dispatcher #####
def create_option_parser(options)
return OptionParser.new do |opts|
nl = "\n" << " " * 37
opts.banner = "Usage: passenger-status [options] [instance name]"
opts.separator ""
opts.separator "Tool for inspecting Phusion Passenger's internal status."
opts.separator ""
opts.separator "Options:"
opts.on("--show=pool|server|backtraces|xml|json|union_station", String,
"Whether to show the pool's contents,#{nl}" <<
"the currently running requests,#{nl}" <<
"the backtraces of all threads or an XML#{nl}" <<
"or JSON description of the pool.") do |what|
if what !~ /\A(pool|server|requests|backtraces|xml|json|union_station)\Z/
STDERR.puts "Invalid argument for --show."
exit 1
else
options[:show] = what
end
end
opts.on("--no-header", "Do not display an informative header#{nl}" <<
"containing the timestamp, version number,#{nl}" <<
"etc.") do
options[:noheader] = true
end
opts.on("--force-colors", "Display colors even if stdout is not a TTY") do
options[:color] = true
end
opts.on("--pid-identifier", "Identify instance by [Watchdog] PID, rather than name.") do
options[:pid_identifier] = true
end
opts.on("--verbose", "-v", "Show verbose information.") do
options[:verbose] = true
end
end
end
def parse_argv
options = DEFAULT_OPTIONS.dup
parser = create_option_parser(options)
begin
parser.parse!
rescue OptionParser::ParseError => e
puts e
puts
puts "Please see '--help' for valid options."
exit 1
end
return options
end
def infer_command
if !ARGV[0] || ARGV[0] !~ /^-/
return [:show_status, ARGV.dup]
else
command_name, *argv = ARGV
if respond_to?("command_#{command_name}")
return [command_name, argv]
else
abort "ERROR: unrecognized command '#{command_name}'"
end
end
end
def start
options = parse_argv
command, argv = infer_command
send("command_#{command}", argv, options)
end
start