lib/puma/control_cli.rb in piesync-puma-3.12.6.1 vs lib/puma/control_cli.rb in piesync-puma-5.4.0.1
- old
+ new
@@ -9,29 +9,55 @@
require 'socket'
module Puma
class ControlCLI
- COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats}
+ # values must be string or nil
+ # value of `nil` means command cannot be processed via signal
+ # @version 5.0.3
+ CMD_PATH_SIG_MAP = {
+ 'gc' => nil,
+ 'gc-stats' => nil,
+ 'halt' => 'SIGQUIT',
+ 'phased-restart' => 'SIGUSR1',
+ 'refork' => 'SIGURG',
+ 'reload-worker-directory' => nil,
+ 'restart' => 'SIGUSR2',
+ 'start' => nil,
+ 'stats' => nil,
+ 'status' => '',
+ 'stop' => 'SIGTERM',
+ 'thread-backtraces' => nil
+ }.freeze
+ # @deprecated 6.0.0
+ COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
+
+ # commands that cannot be used in a request
+ NO_REQ_COMMANDS = %w{refork}.freeze
+
+ # @version 5.0.0
+ PRINTABLE_COMMANDS = %w{gc-stats stats thread-backtraces}.freeze
+
def initialize(argv, stdout=STDOUT, stderr=STDERR)
@state = nil
@quiet = false
@pidfile = nil
@pid = nil
@control_url = nil
@control_auth_token = nil
@config_file = nil
@command = nil
+ @environment = ENV['RACK_ENV'] || ENV['RAILS_ENV']
@argv = argv.dup
@stdout = stdout
@stderr = stderr
@cli_options = {}
opts = OptionParser.new do |o|
- o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{COMMANDS.join("|")})"
+ o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{CMD_PATH_SIG_MAP.keys.join("|")})"
o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
@state = arg
end
@@ -57,29 +83,47 @@
o.on "-F", "--config-file PATH", "Puma config script" do |arg|
@config_file = arg
end
+ o.on "-e", "--environment ENVIRONMENT",
+ "The environment to run the Rack app on (default development)" do |arg|
+ @environment = arg
+ end
+
o.on_tail("-H", "--help", "Show this message") do
@stdout.puts o
exit
end
o.on_tail("-V", "--version", "Show version") do
- puts Const::PUMA_VERSION
+ @stdout.puts Const::PUMA_VERSION
exit
end
end
opts.order!(argv) { |a| opts.terminate a }
opts.parse!
@command = argv.shift
+ # check presence of command
+ unless @command
+ raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
+ end
+
+ unless CMD_PATH_SIG_MAP.key? @command
+ raise "Invalid command: #{@command}"
+ end
+
unless @config_file == '-'
- if @config_file.nil? and File.exist?('config/puma.rb')
- @config_file = 'config/puma.rb'
+ environment = @environment || 'development'
+
+ if @config_file.nil?
+ @config_file = %W(config/puma/#{environment}.rb config/puma.rb).find do |f|
+ File.exist?(f)
+ end
end
if @config_file
config = Puma::Configuration.new({ config_files: [@config_file] }, {})
config.load
@@ -87,23 +131,12 @@
@control_url ||= config.options[:control_url]
@control_auth_token ||= config.options[:control_auth_token]
@pidfile ||= config.options[:pidfile]
end
end
-
- # check present of command
- unless @command
- raise "Available commands: #{COMMANDS.join(", ")}"
- end
-
- unless COMMANDS.include? @command
- raise "Invalid command: #{@command}"
- end
-
rescue => e
@stdout.puts e.message
- @stdout.puts e.backtrace
exit 1
end
def message(msg)
@stdout.puts msg unless @quiet
@@ -121,126 +154,134 @@
@control_url = sf.control_url
@control_auth_token = sf.control_auth_token
@pid = sf.pid
elsif @pidfile
# get pid from pid_file
- @pid = File.open(@pidfile).gets.to_i
+ @pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
end
end
def send_request
uri = URI.parse @control_url
# create server object by scheme
- server = case uri.scheme
- when "tcp"
- TCPSocket.new uri.host, uri.port
- when "unix"
- UNIXSocket.new "#{uri.host}#{uri.path}"
- else
- raise "Invalid scheme: #{uri.scheme}"
- end
+ server =
+ case uri.scheme
+ when 'ssl'
+ require 'openssl'
+ OpenSSL::SSL::SSLSocket.new(
+ TCPSocket.new(uri.host, uri.port),
+ OpenSSL::SSL::SSLContext.new)
+ .tap { |ssl| ssl.sync_close = true } # default is false
+ .tap(&:connect)
+ when 'tcp'
+ TCPSocket.new uri.host, uri.port
+ when 'unix'
+ # check for abstract UNIXSocket
+ UNIXSocket.new(@control_url.start_with?('unix://@') ?
+ "\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}")
+ else
+ raise "Invalid scheme: #{uri.scheme}"
+ end
- if @command == "status"
- message "Puma is started"
+ if @command == 'status'
+ message 'Puma is started'
+ elsif NO_REQ_COMMANDS.include? @command
+ raise "Invalid request command: #{@command}"
else
url = "/#{@command}"
if @control_auth_token
url = url + "?token=#{@control_auth_token}"
end
- server << "GET #{url} HTTP/1.0\r\n\r\n"
+ server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
unless data = server.read
- raise "Server closed connection before responding"
+ raise 'Server closed connection before responding'
end
response = data.split("\r\n")
if response.empty?
raise "Server sent empty response"
end
- (@http,@code,@message) = response.first.split(" ",3)
+ @http, @code, @message = response.first.split(' ',3)
- if @code == "403"
- raise "Unauthorized access to server (wrong auth token)"
- elsif @code == "404"
+ if @code == '403'
+ raise 'Unauthorized access to server (wrong auth token)'
+ elsif @code == '404'
raise "Command error: #{response.last}"
- elsif @code != "200"
+ elsif @code != '200'
raise "Bad response from server: #{@code}"
end
message "Command #{@command} sent success"
- message response.last if @command == "stats" || @command == "gc-stats"
+ message response.last if PRINTABLE_COMMANDS.include?(@command)
end
ensure
- server.close if server && !server.closed?
+ if server
+ if uri.scheme == 'ssl'
+ server.sysclose
+ else
+ server.close unless server.closed?
+ end
+ end
end
def send_signal
unless @pid
- raise "Neither pid nor control url available"
+ raise 'Neither pid nor control url available'
end
begin
+ sig = CMD_PATH_SIG_MAP[@command]
- case @command
- when "restart"
- Process.kill "SIGUSR2", @pid
-
- when "halt"
- Process.kill "QUIT", @pid
-
- when "stop"
- Process.kill "SIGTERM", @pid
-
- when "stats"
- puts "Stats not available via pid only"
+ if sig.nil?
+ @stdout.puts "'#{@command}' not available via pid only"
+ @stdout.flush unless @stdout.sync
return
-
- when "reload-worker-directory"
- puts "reload-worker-directory not available via pid only"
+ elsif sig.start_with? 'SIG'
+ Process.kill sig, @pid
+ elsif @command == 'status'
+ begin
+ Process.kill 0, @pid
+ @stdout.puts 'Puma is started'
+ @stdout.flush unless @stdout.sync
+ rescue Errno::ESRCH
+ raise 'Puma is not running'
+ end
return
-
- when "phased-restart"
- Process.kill "SIGUSR1", @pid
-
- else
- return
end
-
rescue SystemCallError
- if @command == "restart"
+ if @command == 'restart'
start
else
raise "No pid '#{@pid}' found"
end
end
message "Command #{@command} sent success"
end
def run
- return start if @command == "start"
-
+ return start if @command == 'start'
prepare_configuration
- if Puma.windows?
+ if Puma.windows? || @control_url
send_request
else
- @control_url ? send_request : send_signal
+ send_signal
end
rescue => e
message e.message
- message e.backtrace
exit 1
end
- private
+ private
def start
require 'puma/cli'
run_args = []
@@ -248,9 +289,10 @@
run_args += ["-q"] if @quiet
run_args += ["--pidfile", @pidfile] if @pidfile
run_args += ["--control-url", @control_url] if @control_url
run_args += ["--control-token", @control_auth_token] if @control_auth_token
run_args += ["-C", @config_file] if @config_file
+ run_args += ["-e", @environment] if @environment
events = Puma::Events.new @stdout, @stderr
# replace $0 because puma use it to generate restart command
puma_cmd = $0.gsub(/pumactl$/, 'puma')