Class: Magistrate::Process
- Inherits:
-
Object
- Object
- Magistrate::Process
- Defined in:
- lib/magistrate/process.rb
Instance Attribute Summary collapse
-
#daemonize ⇒ Object
readonly
Returns the value of attribute daemonize.
-
#env ⇒ Object
readonly
Returns the value of attribute env.
-
#logs ⇒ Object
readonly
Returns the value of attribute logs.
-
#monitored ⇒ Object
Returns the value of attribute monitored.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#pid_file ⇒ Object
readonly
Returns the value of attribute pid_file.
-
#start_cmd ⇒ Object
readonly
Returns the value of attribute start_cmd.
-
#stop_cmd ⇒ Object
readonly
Returns the value of attribute stop_cmd.
-
#target_state ⇒ Object
Returns the value of attribute target_state.
-
#working_dir ⇒ Object
readonly
Returns the value of attribute working_dir.
Instance Method Summary collapse
- #alive? ⇒ Boolean
- #double_fork(command) ⇒ Object
-
#ensure_stop ⇒ Object
Ensure that a stop command actually stops the process.
-
#initialize(name, options = {}) ⇒ Process
constructor
A new instance of Process.
- #log(str) ⇒ Object
-
#pid ⇒ Object
Fetch the PID from pid_file.
- #running? ⇒ Boolean
-
#signal(sig, target_pid = nil) ⇒ Object
Send the given signal to this process.
-
#single_fork(command) ⇒ Object
single fork self-daemonizing processes we want to wait for them to finish.
-
#spawn(command) ⇒ Object
Fork/exec the given command, returns immediately
command
is the String containing the shell command. - #start ⇒ Object
- #state ⇒ Object
- #status ⇒ Object
- #stop ⇒ Object
-
#supervise! ⇒ Object
This is to be called when we first start managing a worker It will check if the pid exists and if so, is the process responding OK? It will take action based on the target state.
- #write_pid ⇒ Object
Constructor Details
#initialize(name, options = {}) ⇒ Process
Returns a new instance of Process.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/magistrate/process.rb', line 7 def initialize(name, = {}) @name = name @daemonize = [:daemonize] @working_dir = [:working_dir] @start_cmd = [:start_cmd] @pid_path = [:pid_path] if @daemonize @pid_file = File.join(@pid_path, "#{@name}.pid") @stop_signal = [:stop_signal] || 'TERM' else @stop_cmd = [:end_cmd] @pid_file = [:pid_file] end @stop_timeout = 5 @start_timeout = 5 @env = {} @target_state = :unknown @logs = [] end |
Instance Attribute Details
#daemonize ⇒ Object (readonly)
Returns the value of attribute daemonize.
4 5 6 |
# File 'lib/magistrate/process.rb', line 4 def daemonize @daemonize end |
#env ⇒ Object (readonly)
Returns the value of attribute env.
4 5 6 |
# File 'lib/magistrate/process.rb', line 4 def env @env end |
#logs ⇒ Object (readonly)
Returns the value of attribute logs.
4 5 6 |
# File 'lib/magistrate/process.rb', line 4 def logs @logs end |
#monitored ⇒ Object
Returns the value of attribute monitored.
5 6 7 |
# File 'lib/magistrate/process.rb', line 5 def monitored @monitored end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
4 5 6 |
# File 'lib/magistrate/process.rb', line 4 def name @name end |
#pid_file ⇒ Object (readonly)
Returns the value of attribute pid_file.
4 5 6 |
# File 'lib/magistrate/process.rb', line 4 def pid_file @pid_file end |
#start_cmd ⇒ Object (readonly)
Returns the value of attribute start_cmd.
4 5 6 |
# File 'lib/magistrate/process.rb', line 4 def start_cmd @start_cmd end |
#stop_cmd ⇒ Object (readonly)
Returns the value of attribute stop_cmd.
4 5 6 |
# File 'lib/magistrate/process.rb', line 4 def stop_cmd @stop_cmd end |
#target_state ⇒ Object
Returns the value of attribute target_state.
5 6 7 |
# File 'lib/magistrate/process.rb', line 5 def target_state @target_state end |
#working_dir ⇒ Object (readonly)
Returns the value of attribute working_dir.
4 5 6 |
# File 'lib/magistrate/process.rb', line 4 def working_dir @working_dir end |
Instance Method Details
#alive? ⇒ Boolean
255 256 257 258 259 260 261 |
# File 'lib/magistrate/process.rb', line 255 def alive? if p = self.pid !!::Process.kill(0, p) rescue false else false end end |
#double_fork(command) ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/magistrate/process.rb', line 123 def double_fork(command) pid = nil # double fork daemonized processes # we don't want to wait for them to finish r, w = IO.pipe begin opid = fork do STDOUT.reopen(w) r.close pid = self.spawn(command) puts pid.to_s # send pid back to forker end ::Process.waitpid(opid, 0) w.close pid = r.gets.chomp ensure # make sure the file descriptors get closed no matter what r.close rescue nil w.close rescue nil end pid end |
#ensure_stop ⇒ Object
Ensure that a stop command actually stops the process. Force kill if necessary.
Returns nothing
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/magistrate/process.rb', line 197 def ensure_stop log "Ensuring stop..." unless self.pid log "Stop called but pid is unknown" return end # Poll to see if it's dead @stop_timeout.times do begin signal(0) rescue Errno::ESRCH # It died. Good. return end sleep 1 end # last resort signal('KILL') log "Still alive after #{@stop_timeout}s; sent SIGKILL" end |
#log(str) ⇒ Object
31 32 33 |
# File 'lib/magistrate/process.rb', line 31 def log(str) @logs << str end |
#pid ⇒ Object
Fetch the PID from pid_file. If the pid_file does not exist, then use the PID from the last time it was read. If it has never been read, then return nil.
Returns Integer(pid) or nil
237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/magistrate/process.rb', line 237 def pid contents = File.read(@pid_file).strip rescue '' real_pid = contents =~ /^\d+$/ ? contents.to_i : nil if real_pid @pid = real_pid real_pid else @pid end end |
#running? ⇒ Boolean
44 45 |
# File 'lib/magistrate/process.rb', line 44 def running? end |
#signal(sig, target_pid = nil) ⇒ Object
Send the given signal to this process.
Returns nothing
225 226 227 228 229 230 |
# File 'lib/magistrate/process.rb', line 225 def signal(sig, target_pid = nil) target_pid ||= self.pid sig = sig.to_i if sig.to_i != 0 log "Sending signal '#{sig}' to pid #{target_pid}" ::Process.kill(sig, target_pid) rescue nil end |
#single_fork(command) ⇒ Object
single fork self-daemonizing processes we want to wait for them to finish
112 113 114 115 116 117 118 119 120 121 |
# File 'lib/magistrate/process.rb', line 112 def single_fork(command) pid = self.spawn(command) status = ::Process.waitpid2(pid, 0) exit_code = status[1] >> 8 if exit_code != 0 log "Command exited with non-zero code = #{exit_code}" end pid end |
#spawn(command) ⇒ Object
Fork/exec the given command, returns immediately
+command+ is the String containing the shell command
Returns nothing
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/magistrate/process.rb', line 152 def spawn(command) fork do ::Process.setsid dir = @working_dir || '/' Dir.chdir dir #$0 = command $0 = "Magistrate Worker: #{@name}" STDIN.reopen "/dev/null" STDOUT.reopen '/dev/null' STDERR.reopen STDOUT # if self.log_cmd # STDOUT.reopen IO.popen(self.log_cmd, "a") # else # STDOUT.reopen file_in_chroot(self.log), "a" # end # # if err_log_cmd # STDERR.reopen IO.popen(err_log_cmd, "a") # elsif err_log && (log_cmd || err_log != log) # STDERR.reopen file_in_chroot(err_log), "a" # else # STDERR.reopen STDOUT # end # close any other file descriptors 3.upto(256){|fd| IO::new(fd).close rescue nil} if @env && @env.is_a?(Hash) @env.each do |key, value| ENV[key] = value.to_s end end exec command unless command.empty? end end |
#start ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/magistrate/process.rb', line 73 def start log "#{@name} starting" if @daemonize @pid = double_fork(@start_cmd) # TODO: Should check if the pid really exists as we expect write_pid else @pid = single_fork(@start_cmd) end @pid end |
#state ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/magistrate/process.rb', line 47 def state if @target_state == :unmonitored || @target_state == :unknown :unmonitored else if self.alive? :running else :stopped end end end |
#status ⇒ Object
35 36 37 38 39 40 41 42 |
# File 'lib/magistrate/process.rb', line 35 def status { :state => self.state, :target_state => self.target_state, :pid => self.pid, :logs => @logs } end |
#stop ⇒ Object
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/magistrate/process.rb', line 85 def stop if @daemonize signal(@stop_signal, pid) # Poll to see if it's dead @stop_timeout.times do begin ::Process.kill(0, pid) rescue Errno::ESRCH # It died. Good. log "Process stopped" return end sleep 1 end signal('KILL', pid) log "Still alive after #{@stop_timeout}s; sent SIGKILL" else single_fork(@stop_cmd) ensure_stop end end |
#supervise! ⇒ Object
This is to be called when we first start managing a worker It will check if the pid exists and if so, is the process responding OK? It will take action based on the target state
62 63 64 65 66 67 68 69 70 71 |
# File 'lib/magistrate/process.rb', line 62 def supervise! log "Supervising. Is: #{state}. Target: #{@target_state}" if state != @target_state if @target_state == :running start elsif @target_state == :stopped stop end end end |
#write_pid ⇒ Object
249 250 251 252 253 |
# File 'lib/magistrate/process.rb', line 249 def write_pid File.open(@pid_file, 'w') do |f| f.write @pid end end |