Module: Pidify

Defined in:
lib/pidify.rb

Overview

Use the module methods in Pidify to save/delete the PID of a running script, or kill a running script using a saved PID.

Example:

require 'pidify'
Pidify.running?  # => false
Pidify.start
Pidify.running?  # => true
puts "I am running with PID #{Pidify.pid}!"
Pidify.stop
Pidify.running?  # => false

A more useful example:

require 'pidify'
Signal.trap('INT') { Pidify.stop; exit }
module Doer
  def self.start
    puts "starting"
    Pidify.start_as_daemon
    loop do
      puts "hello world"
      sleep 1
    end
  end
end
if ARGV.include? 'stop'
  Pidify.stop 
  puts "Daemon stopped."
else
  puts "Daemon starting."
  Doer.start
end

Class Method Summary collapse

Class Method Details

.daemonizeObject

Daemonizes this process. Does not automatically use a PID file. If you want to use a PID file, you must call Pidify.start after the call to daemonize or use Pidify.start_as_daemon.



213
214
215
216
217
218
219
220
221
# File 'lib/pidify.rb', line 213

def daemonize
  fork and exit
  Process.setsid
  Dir.chdir '/'
  File.umask 0000
  STDIN.reopen "/dev/null"
  STDOUT.reopen "/dev/null", "a"
  STDERR.reopen STDOUT
end

.delete_pidObject

Deletes the PID file. Calling stop calls this automatically, but will also try to send a kill signal to the running process, if it is different from this one. BEWARE that this tries to delete whatever file is returned by pid_file and does no error checking on it! Returns true if the delete was successful, false if there was an error, and nil if the pid file doesn’t exist.



145
146
147
148
149
150
151
152
153
154
# File 'lib/pidify.rb', line 145

def delete_pid
  return nil unless pid_exists?
  begin
    # FIXME: lock first?
    File.delete(pid_file)
    true
  rescue
    false
  end
end

.pidObject

Returns the PID stored in the pid_file (not necessarily the PID of this script). Returns nil if no PID exists or if there is a problem with the read.



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/pidify.rb', line 114

def pid
  return nil unless pid_exists?
  dpid = nil
  begin
    File.open(pid_file, File::RDONLY) { |file| dpid = file.gets.chomp if file.flock(File::LOCK_SH|File::LOCK_NB); file.flock(File::LOCK_UN) }
  rescue
    return nil
  end
  return dpid.to_i if dpid && dpid.to_i > 0
  nil
end

.pid_directoryObject

Returns the Pathname of the PID storage directory (defaults to /var/run).



77
78
79
# File 'lib/pidify.rb', line 77

def pid_directory
  @pid_directory
end

.pid_directory=(dir) ⇒ Object

Sets the PID storage directory (defaults to /var/run). Be VERY CAREFUL using this, as delete_pid will try to delete whatever file it thinks is the pid_file for this script in the pid_directory. It’s probably a good idea not to change this at all.



85
86
87
88
# File 'lib/pidify.rb', line 85

def pid_directory=(dir)
  @pid_directory = Pathname.new(dir) unless dir.kind_of? Pathname
  @pid_directory = dir if dir.kind_of? Pathname
end

.pid_end(signals = %w(SIGTERM SIGQUIT SIGKILL), secs_between_signal = 4) ⇒ Object

Sends each kill signal to the saved PID, pausing for secs_between_signal after each to check if it the process remains running. Stops when the process has ended or when all signals have been tried. Returns true if the process was killed or false otherwise.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/pidify.rb', line 184

def pid_end(signals=%w(SIGTERM SIGQUIT SIGKILL), secs_between_signal=4)
  pid = self.pid
  signals = [ signals ].flatten.map{|sig| sig.to_s}
  existed = false
  signals.each do |sig|
    begin
      Process.kill(sig, pid)
      existed = true
    rescue Errno::ESRCH
      return (existed ? true : nil)
    end
    return true unless running?
    sleep secs_between_signal
    return true unless running?
  end
  not running?
end

.pid_exists?Boolean

Returns true if the pid_file exists for this script.

Returns:

  • (Boolean)


96
97
98
# File 'lib/pidify.rb', line 96

def pid_exists?
  return FileTest.exists?(pid_file)
end

.pid_fileObject

Returns the PID filename as a Pathname.



91
92
93
# File 'lib/pidify.rb', line 91

def pid_file
  @pid_directory + (Pathname.new(@file_name).basename.to_s+'.pid')
end

.running?Boolean

Returns true if the process using pid is running.

Returns:

  • (Boolean)


101
102
103
104
105
106
107
108
109
# File 'lib/pidify.rb', line 101

def running?
  return false unless pid_exists?
  begin
    Process::kill 0, pid
    true
  rescue Errno::ESRCH
    false
  end 
end

.save_pidObject

Saves the PID of this script into the pid_file. Automatically called by start. Returns nil if the pid file already exists. Returns true if successful, false if there was a write problem.



129
130
131
132
133
134
135
136
137
# File 'lib/pidify.rb', line 129

def save_pid
  return nil if pid_exists?
  begin
    File.open(pid_file, File::CREAT|File::EXCL|File::WRONLY) { |file| file.puts $$ if file.flock(File::LOCK_EX); file.flock(File::LOCK_UN) }
    true
  rescue
    false
  end
end

.startObject

Saves the PID of this script into the pid_file by calling save_pid. Raises an exception if pid_exists? returns false. Returns true if successful.



159
160
161
162
# File 'lib/pidify.rb', line 159

def start
  raise "Failed to start: already running (PID file exists)." if pid_exists?
  return true if save_pid
end

.start_as_daemonObject

Like Pidify.start, but first calls Pidify.daemonize. Will fail and raise an exception if Pidify.running? returns true.



204
205
206
207
208
# File 'lib/pidify.rb', line 204

def start_as_daemon
  raise "Failed to start: already running." if running?
  daemonize
  start
end

.stop(signals = %w(SIGTERM SIGQUIT SIGKILL), secs_between_signal = 4) ⇒ Object

Deletes the saved PID file and, if the PID belongs to a process different from this script, sends kill signals to the saved PID using pid_end. Returns true if the process was killed or false otherwise.



167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/pidify.rb', line 167

def stop(signals=%w(SIGTERM SIGQUIT SIGKILL), secs_between_signal=4)
  return false unless pid_exists?
  unless running?
    delete_pid
    return true
  end
  pid = self.pid
  killed = true
  killed = pid_end(signals, secs_between_signal) if pid != $$
  delete_pid if killed == true
  killed
end