Skip to content

Instantly share code, notes, and snippets.

@clicube
Created December 21, 2014 07:48
Embed
What would you like to do?
module StartStopDaemon
# Start or stop daemon process.
#
# * command (String or Symbol):
# [start] Create new instance with arguments ,call #start and create PID file.
# [stop] Call #stop, send TERM signal and remove PID file.
# [restart] Do stop and start.
# [status] Show whether the process is running nor not.
# [-h|--help|help] Show usage.
#
# * options (Hash):
# [:pidfile] Filepath of pidfile. Default is <classname>.pid in current directory.
# [:logfile] Filepath of logfile. Default is <classname>.log in current directory.
# [:workdir] If this is set, working directory will be changed.
# [:synclog] Set to File#sync of logfile. Default is true.
# [:args] Arguments for initialize the extended class.
def start_stop_daemon! command, options={}
opt = Object.new
class << opt
attr_accessor :pidfile, :workdir, :logfile, :synclog, :args
end
options.each do |k,v|
opt.send "#{k}=",v
end
yield opt if block_given?
classname = self.name.split('::').last
opt.pidfile ||= "#{classname}.pid"
opt.logfile ||= "#{classname}.log"
opt.workdir ||= Dir.pwd
opt.synclog ||= true
case command.to_s
when 'start'
start opt
when 'stop'
stop opt
when 'restart'
restart opt
when 'status'
status opt
when 'help', '-h', '--help'
help opt
exit 0
else
puts "Invalid command"
help opt
exit 1
end
end
private
def get_pid pidfile #:nodoc:
return nil unless File.readable?(pidfile)
File.open(pidfile,"r") do |f|
return f.gets.to_i
end
end
def alive? pid #:nodoc:
Process.kill 0, pid
rescue Errno::ESRCH
false
end
def start options #:nodoc:
unless File.writable?(File.dirname(options.logfile))
STDERR.puts "Unable to write log file to #{options.logfile}"
exit 1
end
unless File.writable?(File.dirname(options.pidfile))
STDERR.puts "Unable to write pid file to #{options.pidfile}"
exit 1
end
if (pid = get_pid(options.pidfile)) && alive?(pid)
STDERR.puts "Process is already running (PID: #{pid})"
exit 1
end
fork do
Process.setsid
exit if fork
File.open(options.pidfile, "w") do |f|
f << Process.pid
end
Dir.chdir options.workdir
if options.logfile
old_umask = File.umask 0000
log = File.new(options.logfile, "a")
File.umask old_umask
log.sync = options.synclog
STDIN.reopen "/dev/null"
STDOUT.reopen log
STDERR.reopen STDOUT
end
instance = self.new *(options.args||[])
trap("TERM") do
if defined?(instance.stop) == "method"
instance.stop
end
exit
end
if defined?(instance.start) == "method"
instance.start
end
end
5.times do
sleep 0.1
if (pid = get_pid(options.pidfile)) && alive?(pid)
puts "Started process (PID: #{pid})"
exit 0
end
end
puts "Unable to start process"
exit 1
end
def stop options, on_restart=false #:nodoc:
unless pid = get_pid(options.pidfile)
puts "Unable to read PID file #{options.pidfile}"
exit 1
end
unless alive? pid
puts "Process is not running (PID: #{pid})"
exit 1
end
Process.kill :TERM, pid
5.times do
sleep 0.1
unless alive? pid
File.delete options.pidfile
puts "Stopped process (PID: #{pid})"
if on_restart
return true
else
exit 0
end
end
end
puts "Unable to stop process (PID: #{pid})"
exit 1
end
def restart options #:nodoc:
stop options,true
start options
end
def status options #:nodoc:
unless pid = get_pid(options.pidfile)
puts "Unable to read PID file #{options.pidfile}"
exit 0
end
unless alive? pid
puts "Process is not running (PID: #{pid})"
exit 0
end
puts "process is running (PID: #{pid})"
exit 0
end
def help options #:nodoc:
puts "usage: #{$0} <start|stop|restart|status|help>"
end
end
if $0 == __FILE__
class Neko
extend StartStopDaemon
def initialize *args
puts "initialized with #{args}"
sleep
end
end
Neko.start_stop_daemon! ARGV[0] do |neko|
neko.logfile = File.dirname(__FILE__) + "/neko.log"
neko.pidfile = File.dirname(__FILE__) + "/neko.pid"
neko.args = :nyan
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment