Helper module with specific functions to handle objects that have daemons. The class including it must +extend MiGA::Common::WithDaemonClass+ and define:
#daemon_home
Path to the daemon's
home
#daemon_name
Name of the daemon
#daemon_loop
One loop of the daemon to be repeatedly called
#daemon_first_loop
To be executed before the first call to
#daemon_loop
Process ID of the forked process declaring the daemon alive
Loop counter
Is the daemon active?
# File lib/miga/common/with_daemon.rb, line 46 def active? return false unless File.exist? alive_file (last_alive || Time.new(0)) > Time.now - 60 end
# File lib/miga/common/with_daemon.rb, line 30 def alive_file self.class.alive_file(daemon_home) end
Launches the task
with options opts
(as
command-line arguments) and returns the process ID as an Integer. If
wait
it waits for the process to complete, immediately returns
otherwise. Supported tasks: start, run, stop, status.
# File lib/miga/common/with_daemon.rb, line 127 def daemon(task, opts = [], wait = true) MiGA::MiGA.DEBUG "#{self.class}#daemon #{task} #{opts}" task = task.to_sym raise "Unsupported task: #{task}" unless respond_to? task return send(task, opts, wait) unless %[start run].include? task # start & run: options = default_options opts.unshift(task.to_s) options[:ARGV] = opts # This additional degree of separation below was introduced so the Daemons # package doesn't kill the parent process in workflows. pid = fork { launch_daemon_proc(options) } Process.wait(pid) if wait pid end
Tell the world that you're alive.
# File lib/miga/common/with_daemon.rb, line 54 def declare_alive if active? raise "Trying to declare alive an active daemon, if you think this is a" " mistake please remove #{alive_file} or try again in 1 minute" end @declare_alive_pid = fork { declare_alive_loop } sleep(1) # <- to wait for the process check end
Loop checking if the process with PID pid
is still alive. By
default, the parent process. Do not use directly, use
declare_alive
instead. Returns a symbol indicating the reason
to stop:
:no_home
Daemon's home does not exist
:no_process_alive
Process is not currently running
:termination_file
Found termination file
# File lib/miga/common/with_daemon.rb, line 71 def declare_alive_loop(pid = Process.ppid) i = -1 loop do i += 1 return :no_home unless Dir.exist? daemon_home return :no_process_alive unless process_alive? pid write_alive_file if i % 30 == 0 return :termination_file if termination_file? pid sleep(1) end end
Returns Hash containing the default options for the daemon.
# File lib/miga/common/with_daemon.rb, line 115 def default_options { dir_mode: :normal, dir: daemon_home, multiple: false, log_output: true, stop_proc: :terminate } end
One loop, returns a boolean indicating if the execution should continue
# File lib/miga/common/with_daemon.rb, line 187 def in_loop if loop_i.nil? declare_alive daemon_first_loop @loop_i = -1 end @loop_i += 1 daemon_loop end
When was the daemon last seen active?
# File lib/miga/common/with_daemon.rb, line 40 def last_alive self.class.last_alive(daemon_home) end
Pass daemon options to Daemons
. Do not use directly, use
daemon
instead.
# File lib/miga/common/with_daemon.rb, line 169 def launch_daemon_proc(options) Daemons.run_proc("#{daemon_name}", options) { while in_loop; end } end
# File lib/miga/common/with_daemon.rb, line 22 def output_file File.join(daemon_home, "#{daemon_name}.output") end
# File lib/miga/common/with_daemon.rb, line 18 def pid_file File.join(daemon_home, "#{daemon_name}.pid") end
Check if the process with PID pid
is still alive, call
terminate
otherwise.
# File lib/miga/common/with_daemon.rb, line 92 def process_alive?(pid) Process.kill(0, pid) true rescue Errno::ESRCH, Errno::EPERM, Errno::ENOENT terminate false end
Initializes the daemon on top with opts
# File lib/miga/common/with_daemon.rb, line 181 def run(opts = [], wait = true) daemon(:run, opts, wait) end
Initializes the daemon with opts
# File lib/miga/common/with_daemon.rb, line 175 def start(opts = [], wait = true) daemon(:start, opts, wait) end
Returns the status of the daemon with opts
# File lib/miga/common/with_daemon.rb, line 159 def status(opts = [], wait = true) if active? say "Running with pid #{File.size?(pid_file) ? File.read(pid_file) : '?'}" else say 'Not running' end end
Stops the daemon with opts
# File lib/miga/common/with_daemon.rb, line 146 def stop(opts = [], wait = true) if active? say 'Sending termination message' FileUtils.touch(terminate_file) sleep(0.5) while active? if wait File.unlink(pid_file) if File.exist?(pid_file) else say 'No running instances' end end
Declares a daemon termination. Do not use, directly, use stop instead.
# File lib/miga/common/with_daemon.rb, line 199 def terminate unless declare_alive_pid.nil? Process.kill(9, declare_alive_pid) @declare_alive_pid = nil end File.rename(alive_file, terminated_file) if File.exist? alive_file end
# File lib/miga/common/with_daemon.rb, line 26 def terminate_file File.join(daemon_home, 'terminate-daemon') end
# File lib/miga/common/with_daemon.rb, line 34 def terminated_file self.class.terminated_file(daemon_home) end
Check if a termination file exists and terminate process with PID
pid
if it does. Do not kill any process if pid
is
nil
# File lib/miga/common/with_daemon.rb, line 103 def termination_file?(pid) return false unless File.exist? terminate_file say 'Found termination file, terminating' File.unlink(terminate_file) terminate Process.kill(9, pid) unless pid.nil? true end
# File lib/miga/common/with_daemon.rb, line 85 def write_alive_file File.open(alive_file, 'w') { |fh| fh.print Time.now.to_s } end