aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/commands/process/reaper.rb
blob: 1a08cb1defc9f5fb739186b77af83db404939e79 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
require 'optparse'
require 'net/http'
require 'uri'

if RUBY_PLATFORM =~ /mswin32/ then abort("Reaper is only for Unix") end

class ProgramProcess
  class << self
    def process_keywords(action, *keywords)
      processes = keywords.collect { |keyword| find_by_keyword(keyword) }.flatten

      if processes.empty?
        puts "Couldn't find any process matching: #{keywords.join(" or ")}"
      else
        processes.each do |process|
          puts "#{action.capitalize}ing #{process}"
          process.send(action)
        end
      end      
    end

    def find_by_keyword(keyword)
      process_lines_with_keyword(keyword).split("\n").collect { |line|
        next if line.include?("inq") || line.include?("ps -ax") || line.include?("grep")
        pid, *command = line.split
        new(pid, command.join(" "))
      }.compact
    end

    private
      def process_lines_with_keyword(keyword)
        `ps -ax -o 'pid command' | grep #{keyword}`
      end
  end

  def initialize(pid, command)
    @pid, @command = pid, command
  end

  def find
  end

  def reload
    `kill -s HUP #{@pid}`
  end

  def graceful
    `kill -s TERM #{@pid}`
  end

  def kill
    `kill -9 #{@pid}`
  end

  def usr1
    `kill -s USR1 #{@pid}`
  end

  def restart
    `kill -s USR2 #{@pid}`
  end

  def to_s
    "[#{@pid}] #{@command}"
  end
end

OPTIONS = {
  :action      => "restart",
  :dispatcher  => File.expand_path(RAILS_ROOT + '/public/dispatch.fcgi')
}

ARGV.options do |opts|
  opts.banner = "Usage: reaper [options]"

  opts.separator ""

  opts.on <<-EOF
  Description:
    The reaper is used to restart, reload, gracefully exit, and forcefully exit FCGI processes
    running a Rails Dispatcher. This is commonly done when a new version of the application
    is available, so the existing processes can be updated to use the latest code.

    The reaper actions are:

    * restart : Restarts the application by reloading both application and framework code
    * reload  : Only reloads the application, but not the framework (like the development environment)
    * graceful: Marks all of the processes for exit after the next request
    * kill    : Forcefully exists all processes regardless of whether they're currently serving a request

    Restart is the most common and default action.

  Examples:
    reaper # restarts the default dispatcher
    reaper -a reload
    reaper -a exit -d /my/special/dispatcher.fcgi
  EOF

  opts.on("  Options:")

  opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String)  { |OPTIONS[:action]| }
  opts.on("-d", "--dispatcher=path", "default: #{OPTIONS[:dispatcher]}", String)                 { |OPTIONS[:dispatcher]| }

  opts.separator ""

  opts.on("-h", "--help", "Show this help message.") { puts opts; exit }

  opts.parse!
end

ProgramProcess.process_keywords(OPTIONS[:action], OPTIONS[:dispatcher])