Permalink
Browse files

Begin porting passenger-status to the new infrastructure.

  • Loading branch information...
1 parent 445cd24 commit 9e15dbd8888e1059d6436af1f3852c65f52f802e @FooBarWidget FooBarWidget committed Sep 4, 2009
View
@@ -1,3 +1,9 @@
+Release 3.0.0
+-------------
+
+ * Caveat: command line tools are not compatible with earlier Phusion
+ Passenger versions, so do not attempt this!
+
Release 2.2.5
-------------
@@ -134,4 +140,4 @@ Release 2.2.5
Older releases
--------------
-Please consult the blog posts on http://blog.phusion.nl/ for the information about older releases.
+Please consult the blog posts on http://blog.phusion.nl/ for the information about older releases.
View
@@ -36,13 +36,13 @@ YELLOW = "\e[33m"
BLACK_BG = "\e[40m"
BLUE_BG = "\e[44m"
-def show_status(control_process, options = {})
+def show_status(server_instance, options = {})
case options[:show]
when 'pool'
begin
- text = control_process.status
+ text = server_instance.status
rescue SystemCallError => e
- STDERR.puts "*** ERROR: Cannot query status for Passenger instance #{control_process.pid}:"
+ STDERR.puts "*** ERROR: Cannot query status for Phusion Passenger instance #{server_instance.pid}:"
STDERR.puts e.to_s
exit 2
end
@@ -54,7 +54,7 @@ def show_status(control_process, options = {})
when 'backtraces'
begin
- text = control_process.backtraces
+ text = server_instance.backtraces
rescue SystemCallError => e
STDERR.puts "*** ERROR: Cannot query status for Passenger instance #{control_process.pid}:"
STDERR.puts e.to_s
@@ -99,26 +99,26 @@ def start
end
if ARGV.empty?
- control_processes = ControlProcess.list
- if control_processes.empty?
+ server_instances = ServerInstance.list
+ if server_instances.empty?
STDERR.puts("ERROR: Phusion Passenger doesn't seem to be running.")
exit 2
- elsif control_processes.size == 1
- show_status(control_processes.first, options)
+ elsif server_instances.size == 1
+ show_status(server_instances.first, options)
else
puts "It appears that multiple Passenger instances are running. Please select a"
puts "specific one by running:"
puts
puts " passenger-status <PID>"
puts
puts "The following Passenger instances are running:"
- control_processes.each do |control|
- puts " PID: #{control.pid}"
+ server_instances.each do |instance|
+ puts " PID: #{instance.pid}"
end
exit 1
end
else
- show_status(ControlProcess.for_pid(ARGV[0].to_i), options)
+ show_status(ServerInstance.for_pid(ARGV[0].to_i), options)
end
end
@@ -277,7 +277,7 @@ class MessageServer {
break;
}
- P_TRACE(4, "MessageServer client " << commonContext.name() <<
+ P_TRACE(0, "MessageServer client " << commonContext.name() <<
": received message: " << toString(args));
UPDATE_TRACE_POINT();
@@ -44,6 +44,7 @@ class ThreadStatusServer: public MessageServer::Handler {
{
TRACE_POINT();
if (args[0] == "backtraces") {
+ UPDATE_TRACE_POINT();
commonContext.channel.writeScalar(oxt::thread::all_backtraces());
}
return true;
View
@@ -354,6 +354,8 @@ createPassengerTempDir(const string &parentDir, bool userSwitching,
string tmpDir(getPassengerTempDir(false, parentDir));
uid_t lowestUid;
gid_t lowestGid;
+ string structureVersionFile;
+ FILE *f;
determineLowestUserAndGroup(lowestUser, lowestUid, lowestGid);
@@ -365,6 +367,27 @@ createPassengerTempDir(const string &parentDir, bool userSwitching,
*/
makeDirTree(tmpDir, "u=wxs,g=x,o=x");
+ /* Write structure version file. If you change the version here don't forget
+ * to do it in lib/phusion_passenger/admin_tools/server_instance.rb too.
+ */
+ structureVersionFile = tmpDir + "/structure_version.txt";
+ f = fopen(structureVersionFile.c_str(), "w");
+ if (f != NULL) {
+ fprintf(f, "1,0"); // major,minor
+ if (fflush(f) == EOF) {
+ int e = errno;
+ fclose(f);
+ throw FileSystemException("Cannot write to file " + structureVersionFile,
+ e, structureVersionFile);
+ } else {
+ fclose(f);
+ }
+ } else {
+ int e = errno;
+ throw FileSystemException("Cannot create file " + structureVersionFile,
+ e, structureVersionFile);
+ }
+
/* We want this upload buffer directory to be only accessible by the web server's
* worker processs.
*
View
@@ -306,8 +306,9 @@ string getPassengerTempDir(bool bypassCache = false, const string &parentDir = "
*/
void setPassengerTempDir(const string &dir);
-/* Create a temporary directory for storing Phusion Passenger-specific temp files,
- * such as temporarily buffered uploads, sockets for backend processes, etc.
+/* Create a temporary directory for storing Phusion Passenger instance-specific
+ * temp files, such as temporarily buffered uploads, sockets for backend
+ * processes, etc.
* The directory that will be created is the one returned by
* <tt>getPassengerTempDir(false, parentDir)</tt>. This call stores the path to
* this temp directory in an internal variable, so that subsequent calls to
@@ -348,12 +348,12 @@ start_helper_server(ngx_cycle_t *cycle)
} while (ret == -1 && errno == EINTR);
close(feedback_pipe[0]);
- /* Create the file passenger_temp_dir + "/control_process.pid"
+ /* Create the file passenger_temp_dir + "/instance.pid"
* and make it writable by the worker processes. This is because
* save_master_process_pid is run after Nginx has lowered privileges.
*/
last = ngx_snprintf(filename, sizeof(filename) - 1,
- "%s/control_process.pid", passenger_temp_dir);
+ "%s/instance.pid", passenger_temp_dir);
*last = (u_char) '\0';
f = fopen((const char *) filename, "w");
if (f != NULL) {
@@ -387,9 +387,9 @@ start_helper_server(ngx_cycle_t *cycle)
*
* A bug/limitation in Nginx doesn't allow us to initialize the temp dir *after*
* Nginx has daemonized, so the temp dir's filename contains Nginx's PID before
- * daemonization. Normally PhusionPassenger::AdminTools::ControlProcess (used
+ * daemonization. Normally PhusionPassenger::AdminTools::ServerInstance (used
* by e.g. passenger-status) will think that the temp dir is stale because the
- * PID in the filename doesn't exist. This PID file tells AdminTools::ControlProcess
+ * PID in the filename doesn't exist. This PID file tells AdminTools::ServerInstance
* what the actual PID is.
*/
static ngx_int_t
@@ -403,7 +403,7 @@ save_master_process_pid(ngx_cycle_t *cycle) {
}
last = ngx_snprintf(filename, sizeof(filename) - 1,
- "%s/control_process.pid", passenger_temp_dir);
+ "%s/instance.pid", passenger_temp_dir);
*last = (u_char) '\0';
f = fopen((const char *) filename, "w");
@@ -30,7 +30,23 @@
module PhusionPassenger
module AdminTools
-class ControlProcess
+class ServerInstance
+ # Increment this number if the server instance directory structure has changed in an incompatible way.
+ DIRECTORY_STRUCTURE_MAJOR_VERSION = 1
+ # Increment this number if new features have been added to the server instance directory structure,
+ # in a backwards compatible way.
+ DIRECTORY_STRUCTURE_MINOR_VERSION = 0
+
+ STALE_TIME_THRESHOLD = 60
+
+ class StaleDirectoryError < StandardError
+ end
+ class CorruptedDirectoryError < StandardError
+ end
+ class UnsupportedStructureVersionError < StandardError
+ end
+
+ # TODO: really need to do something about the terminology. it should be "backend process" or something.
class Instance
attr_accessor :pid, :socket_name, :socket_type, :sessions, :uptime
INT_PROPERTIES = [:pid, :sessions]
@@ -39,49 +55,67 @@ class Instance
attr_accessor :path
attr_accessor :pid
- def self.list(clean_stale = true)
- results = []
+ def self.list(clean_stale_or_corrupted = true)
+ instances = []
Dir["#{AdminTools.tmpdir}/passenger.*"].each do |dir|
next if dir !~ /passenger.(\d+)\Z/
begin
- results << ControlProcess.new(dir)
- rescue ArgumentError
- # Stale Passenger temp folder. Clean it up if instructed.
- if clean_stale
- puts "*** Cleaning stale folder #{dir}"
+ instances << ServerInstance.new(dir)
+ rescue StaleDirectoryError, CorruptedDirectoryError
+ if clean_stale_or_corrupted &&
+ File.stat(dir).ctime < current_time - STALE_TIME_THRESHOLD
+ log_cleaning_action(dir)
FileUtils.chmod_R(0700, dir) rescue nil
FileUtils.rm_rf(dir)
end
+ rescue UnsupportedStructureVersionError
+ # Do nothing.
end
end
- return results
+ return instances
end
def self.for_pid(pid)
return list(false).find { |c| c.pid == pid }
end
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
- if data.empty?
- raise ArgumentError, "'#{path}' is not a valid control process directory."
- else
- @pid = data.to_i
- end
+
+ if File.exist?("#{path}/instance.pid")
+ data = File.read("#{path}/instance.pid").strip
+ @pid = data.to_i
else
path =~ /passenger.(\d+)\Z/
@pid = $1.to_i
end
- if !AdminTools.process_is_alive?(@pid)
- raise ArgumentError, "There is no control process with PID #{@pid}."
+ if @pid == 0
+ raise CorruptedDirectoryError, "Instance directory contains corrupted instance.pid file."
+ elsif !AdminTools.process_is_alive?(@pid)
+ raise StaleDirectoryError, "There is no instance with PID #{@pid}."
+ end
+
+ if !File.exist?("#{path}/structure_version.txt")
+ raise CorruptedDirectoryError, "Directory doesn't contain a structure version specification file."
+ else
+ version_data = File.read("#{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, "Directory doesn't contain a valid structure version specification file."
+ end
+ major = major.to_i
+ minor = minor.to_i
+ if major != DIRECTORY_STRUCTURE_MAJOR_VERSION || minor > DIRECTORY_STRUCTURE_MINOR_VERSION
+ raise UnsupportedStructureVersionError, "Unsupported directory structure version."
+ end
end
end
def status
connect do |channel|
- channel.write("status")
+ channel.write("inspect")
+ check_security_response(channel)
return channel.read_scalar
end
end
@@ -93,9 +127,10 @@ def backtraces
end
end
- def xml
+ def to_xml
connect do |channel|
- channel.write("status_xml")
+ channel.write("toXml", true)
+ check_security_response(channel)
return channel.read_scalar
end
end
@@ -136,14 +171,40 @@ def instances
end
private
+ def self.log_cleaning_action(dir)
+ puts "*** Cleaning stale folder #{dir}"
+ end
+
+ def self.current_time
+ Time.now
+ end
+
+ class << self;
+ private :log_cleaning_action
+ private :current_time
+ end
+
def connect
channel = MessageChannel.new(UNIXSocket.new("#{path}/master/pool_controller.socket"))
begin
+ @username = '_passenger-status'
+ @password = '_passenger-status'
+ channel.write_scalar(@username)
+ channel.write_scalar(@password)
yield channel
ensure
channel.close
end
end
+
+ def check_security_response(channel)
+ result = channel.read
+ if result.nil?
+ raise EOFError
+ elsif result[0] != "Passed security"
+ raise SecurityError, result[0]
+ end
+ end
end
end # module AdminTools
@@ -93,6 +93,7 @@ def initialize(io)
def read
buffer = ''
while buffer.size < HEADER_SIZE
+ puts(HEADER_SIZE - buffer.size)
buffer << @io.readpartial(HEADER_SIZE - buffer.size)
end
Oops, something went wrong.

0 comments on commit 9e15dbd

Please sign in to comment.