Usage:
SpoonDaemon.new('start', 'path/to/my/script.rb', 'tmp/pids')
require 'spoon' | |
class SpoonDaemon | |
def initialize(cmd, script, pid_dir) | |
@script = script | |
@pid_dir = pid_dir | |
@pid_path = File.join(@pid_dir, "#{File.basename(script)}.pid") | |
case cmd | |
when 'start' | |
start | |
when 'stop' | |
stop | |
when 'restart' | |
stop | |
start | |
else | |
STDERR.puts "Usage: <script> <start|stop|restart>" | |
exit! | |
end | |
end | |
def create_pid(pid) | |
begin | |
open(@pid_path, 'w') do |f| | |
f.puts pid | |
end | |
rescue => e | |
STDERR.puts "Error: Unable to open #{@pid_path} for writing:\n\t" + | |
"(#{e.class}) #{e.message}" | |
exit! | |
end | |
end | |
def get_pid | |
pid = nil | |
open(@pid_path, 'r') do |f| | |
pid = f.readline.to_s.gsub(/[^0-9]/,'') | |
end | |
pid.to_i | |
rescue Errno::ENOENT => e | |
nil | |
end | |
def remove_pidfile | |
File.unlink(@pid_path) | |
rescue => e | |
STDERR.puts "ERROR: Unable to unlink #{path}:\n\t" + | |
"(#{e.class}) #{e.message}" | |
exit | |
end | |
def process_exists? | |
pid = get_pid | |
return false unless pid | |
Process.kill(0, pid) | |
true | |
rescue Errno::ESRCH, TypeError # "PID is NOT running or is zombied | |
false | |
rescue Errno::EPERM | |
STDERR.puts "No permission to query #{pid}!"; | |
false | |
end | |
def stop | |
pid = get_pid | |
Process.kill("TERM", pid) | |
remove_pidfile | |
rescue Errno::ESRCH | |
abort "No process to kill" | |
end | |
def start | |
abort "Process already running!" if process_exists? | |
if pid = Spoon.spawnp(@script) | |
create_pid(pid) | |
else | |
Process.setsid | |
end | |
end | |
end |