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)

- +SecurityError+: The server denied our authentication credentials.
- +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

FIXME: probably broken
def get_wait_list_size
	return stats.get_wait_list_size
end

def groups(client)

FIXME: probably broken
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)

FIXME: probably broken
def processes(client)
	return groups(client).map do |group|
		group.processes
	end.flatten
end

def stats

FIXME: probably broken
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