class PhusionPassenger::AdminTools::ServerInstance
def self.current_time
def self.current_time Time.now end
def self.for_pid(pid, options = {})
def self.for_pid(pid, options = {}) return list(options).find { |c| c.pid == pid } end
def self.list(options = {})
def self.list(options = {}) options = { :clean_stale_or_corrupted => true }.merge(options) instances = [] Dir["#{AdminTools.tmpdir}/passenger.*"].each do |dir| next if File.basename(dir) !~ /passenger\.#{PhusionPassenger::SERVER_INSTANCE_DIR_STRUCTURE_MAJOR_VERSION}\.(\d+)\.(.+)\Z/ minor = $1 next if minor.to_i > PhusionPassenger::SERVER_INSTANCE_DIR_STRUCTURE_MINOR_VERSION begin instances << ServerInstance.new(dir) rescue StaleDirectoryError, CorruptedDirectoryError if options[:clean_stale_or_corrupted] && File.stat(dir).mtime < current_time - STALE_TIME_THRESHOLD log_cleaning_action(dir) FileUtils.chmod_R(0700, dir) rescue nil FileUtils.rm_rf(dir) end rescue UnsupportedGenerationStructureVersionError, GenerationsAbsentError # Do nothing. end end return instances end
def self.log_cleaning_action(dir)
def self.log_cleaning_action(dir) puts "*** Cleaning stale folder #{dir}" end
def analytics_log_dir
def analytics_log_dir return File.read("#{@generation_path}/analytics_log_dir.txt") rescue Errno::ENOENT return nil end
def connect(options)
- +EOFError+: The server unexpectedly closed the connection during authentication.
- +RoleDeniedError+: The user that the current process is as is not authorized to utilize the given role.
- +ArgumentError+: Unsupported role
Raises:
def connect(options) if options[:role] username, password, default_socket_name = infer_connection_info_from_role(options[:role]) socket_name = options[:socket_name] || default_socket_name else username = options[:username] password = options[:password] socket_name = options[:socket_name] || "helper_admin" raise ArgumentError, "Either the :role or :username must be set" if !username raise ArgumentError, ":password must be set" if !password end client = MessageClient.new(username, password, "unix:#{@generation_path}/#{socket_name}") if block_given? begin yield client ensure client.close end else return client end end
def get_wait_list_size
def get_wait_list_size return stats.get_wait_list_size end
def groups(client)
def groups(client) doc = REXML::Document.new(client.pool_xml) groups = [] doc.elements.each("info/supergroups/supergroup/group") do |group_xml| group = Group.new(group_xml.elements["app_root"].text, group_xml.elements["name"].text, group_xml.elements["environment"].text, !!group_xml.elements["spawning"]) group_xml.elements.each("processes/process") do |process_xml| process = Process.new(group) process_xml.elements.each do |element| if element.name == "sockets" element.elements.each("socket") do |server_socket| name = server_socket.elements["name"].text.to_sym address = server_socket.elements["address"].text protocol = server_socket.elements["protocol"].text process.server_sockets[name] = OpenStruct.new( :name => name, :address => address, :protocol => protocol ) end else if process.respond_to?("#{element.name}=") if Process::INT_PROPERTIES.include?(element.name.to_sym) value = element.text.to_i elsif Process::BOOL_PROPERTIES.include?(element.name.to_sym) value = element.text == "true" else value = element.text end process.send("#{element.name}=", value) end end end group.processes << process end groups << group end return groups end
def helper_agent_pid
def helper_agent_pid return File.read("#{@generation_path}/helper_agent.pid").strip.to_i end
def infer_connection_info_from_role(role)
def infer_connection_info_from_role(role) case role when :passenger_status username = "_passenger-status" begin filename = "#{@generation_path}/passenger-status-password.txt" password = File.open(filename, "rb") do |f| f.read end rescue Errno::EACCES raise RoleDeniedError rescue Errno::ENOENT raise CorruptedDirectoryError end return [username, password, "helper_admin"] else raise ArgumentError, "Unsupported role #{role}" end end
def initialize(path)
def initialize(path) raise ArgumentError, "Path may not be nil." if path.nil? @path = path if File.exist?("#{path}/control_process.pid") data = File.read("#{path}/control_process.pid").strip @pid = data.to_i else path =~ /passenger\.\d+\.\d+\.(\d+)\Z/ @pid = $1.to_i end generations = Dir["#{path}/generation-*"] if generations.empty? raise GenerationsAbsentError, "There are no generation subdirectories in this instance directory." end highest_generation_number = 0 generations.each do |generation| File.basename(generation) =~ /(\d+)/ generation_number = $1.to_i if generation_number > highest_generation_number highest_generation_number = generation_number end end @generation_path = "#{path}/generation-#{highest_generation_number}" if !File.exist?("#{@generation_path}/structure_version.txt") raise CorruptedDirectoryError, "The generation directory doesn't contain a structure version specification file." end version_data = File.read("#{@generation_path}/structure_version.txt").strip major, minor = version_data.split(".", 2) if major.nil? || minor.nil? || major !~ /\A\d+\Z/ || minor !~ /\A\d+\Z/ raise CorruptedDirectoryError, "The generation directory doesn't contain a valid structure version specification file." end major = major.to_i minor = minor.to_i if major != PhusionPassenger::SERVER_INSTANCE_DIR_GENERATION_STRUCTURE_MAJOR_VERSION || minor > PhusionPassenger::SERVER_INSTANCE_DIR_GENERATION_STRUCTURE_MINOR_VERSION raise UnsupportedGenerationStructureVersionError, "Unsupported generation directory structure version." end if @pid == 0 raise CorruptedDirectoryError, "Instance directory contains corrupted control_process.pid file." elsif !AdminTools.process_is_alive?(@pid) raise StaleDirectoryError, "There is no instance with PID #{@pid}." end end
def processes(client)
def processes(client) return groups(client).map do |group| group.processes end.flatten end
def stats
def stats doc = REXML::Document.new(xml) stats = Stats.new stats.max = doc.elements["info/max"].text.to_i stats.usage = doc.elements["info/usage"].text.to_i stats.get_wait_list_size = doc.elements["info/get_wait_list_size"].text.to_i return stats end
def web_server_config_files
def web_server_config_files config_files = File.read("#{@generation_path}/config_files.txt").split("\n") config_files.map! do |filename| filename.strip end config_files.reject do |filename| filename.empty? end return config_files end
def web_server_description
def web_server_description return File.read("#{@generation_path}/web_server.txt") end