Class: Puma::ControlCLI
Relationships & Source Files | |
Inherits: | Object |
Defined in: | lib/puma/control_cli.rb |
Constant Summary
-
CMD_PATH_SIG_MAP =
values must be string or nil value of
nil
means command cannot be processed via signal{ 'gc' => nil, 'gc-stats' => nil, 'halt' => 'SIGQUIT', 'info' => 'SIGINFO', 'phased-restart' => 'SIGUSR1', 'refork' => 'SIGURG', 'reload-worker-directory' => nil, 'reopen-log' => 'SIGHUP', 'restart' => 'SIGUSR2', 'start' => nil, 'stats' => nil, 'status' => '', 'stop' => 'SIGTERM', 'thread-backtraces' => nil, 'worker-count-down' => 'SIGTTOU', 'worker-count-up' => 'SIGTTIN' }.freeze
-
NO_REQ_COMMANDS =
commands that cannot be used in a request
%w[info reopen-log worker-count-down worker-count-up].freeze
-
PRINTABLE_COMMANDS =
%w[gc-stats stats thread-backtraces].freeze
Class Method Summary
Instance Method Summary
Constructor Details
.new(argv, stdout = STDOUT, stderr = STDERR, env: ENV) ⇒ ControlCLI
# File 'lib/puma/control_cli.rb', line 40
def initialize(argv, stdout=STDOUT, stderr=STDERR, env: ENV) @state = nil @quiet = false @pidfile = nil @pid = nil @control_url = nil @control_auth_token = nil @config_file = nil @command = nil @environment = env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'] @argv = argv.dup @stdout = stdout @stderr = stderr @cli_options = {} opts = OptionParser.new do |o| o. = "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 o.on "-Q", "--quiet", "Do not display messages" do |arg| @quiet = true end o.on "-P", "--pidfile PATH", "Pid file" do |arg| @pidfile = arg end o.on "-p", "--pid PID", "Pid" do |arg| @pid = arg.to_i end o.on "-C", "--control-url URL", "The bind url to use for the control server" do |arg| @control_url = arg end o.on "-T", "--control-token TOKEN", "The token to use as authentication for the control server" do |arg| @control_auth_token = arg end 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 @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 == '-' 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 require_relative 'configuration' require_relative 'log_writer' config = Puma::Configuration.new({ config_files: [@config_file] }, {} , env) config.load @state ||= config. [:state] @control_url ||= config. [:control_url] @control_auth_token ||= config. [:control_auth_token] @pidfile ||= config. [:pidfile] end end rescue => e @stdout.puts e. exit 1 end
Instance Method Details
#message(msg)
[ GitHub ]# File 'lib/puma/control_cli.rb', line 143
def (msg) @stdout.puts msg unless @quiet end
#prepare_configuration
[ GitHub ]# File 'lib/puma/control_cli.rb', line 147
def prepare_configuration if @state unless File.exist? @state raise "State file not found: #{@state}" end require_relative 'state_file' sf = Puma::StateFile.new sf.load @state @control_url = sf.control_url @control_auth_token = sf.control_auth_token @pid = sf.pid elsif @pidfile # get pid from pid_file @pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i end end
#run
[ GitHub ]# File 'lib/puma/control_cli.rb', line 277
def run return start if @command == 'start' prepare_configuration if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command) send_request else send_signal end rescue => e e. exit 1 end
#send_request
[ GitHub ]# File 'lib/puma/control_cli.rb', line 167
def send_request uri = URI.parse @control_url host = uri.host # create server object by scheme server = case uri.scheme when 'ssl' require 'openssl' host = host[1..-2] if host&.start_with? '[' OpenSSL::SSL::SSLSocket.new( TCPSocket.new(host, uri.port), OpenSSL::SSL::SSLContext.new) .tap { |ssl| ssl.sync_close = true } # default is false .tap(&:connect) when 'tcp' host = host[1..-2] if host&.start_with? '[' TCPSocket.new host, uri.port when 'unix' # check for abstract UNIXSocket UNIXSocket.new(@control_url.start_with?('unix://@') ? "\0#{host}#{uri.path}" : "#{host}#{uri.path}") else raise "Invalid scheme: #{uri.scheme}" end if @command == 'status' 'Puma is started' else url = "/#{@command}" if @control_auth_token url = url + "?token=#{@control_auth_token}" end server.syswrite "GET #{url} HTTP/1.0\r\n\r\n" unless data = server.read 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) if @code == '403' raise 'Unauthorized access to server (wrong auth token)' elsif @code == '404' raise "Command error: #{response.last}" elsif @code != '200' raise "Bad response from server: #{@code}" end "Command #{@command} sent success" response.last if PRINTABLE_COMMANDS.include?(@command) end ensure if server if uri.scheme == 'ssl' server.sysclose else server.close unless server.closed? end end end
#send_signal
[ GitHub ]# File 'lib/puma/control_cli.rb', line 238
def send_signal unless @pid raise 'Neither pid nor control url available' end begin sig = CMD_PATH_SIG_MAP[@command] if sig.nil? @stdout.puts "'#{@command}' not available via pid only" @stdout.flush unless @stdout.sync return elsif sig.start_with? 'SIG' if Signal.list.key? sig.sub(/\ASIG/, '') Process.kill sig, @pid else raise "Signal '#{sig}' not available'" end 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 end rescue SystemCallError if @command == 'restart' start else raise "No pid '#{@pid}' found" end end "Command #{@command} sent success" end
#start (private)
[ GitHub ]# File 'lib/puma/control_cli.rb', line 293
def start require_relative 'cli' run_args = [] run_args += ["-S", @state] if @state 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 log_writer = Puma::LogWriter.new(@stdout, @stderr) # replace $0 because puma use it to generate restart command puma_cmd = $0.gsub(/pumactl$/, 'puma') $0 = puma_cmd if File.exist?(puma_cmd) cli = Puma::CLI.new run_args, log_writer cli.run end