module MiGA::Common::WithDaemon
def active?
#
def active? return false unless File.exist? alive_file (last_alive || Time.new(0)) > Time.now - 60 end
def alive_file
def alive_file self.class.alive_file(daemon_home) end
def daemon(task, opts = [], wait = true)
complete, immediately returns otherwise.
returns the process ID as an Integer. If +wait+ it waits for the process to
Launches the +task+ with options +opts+ (as command-line arguments) and
#
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 %i[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
def declare_alive
#
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
def declare_alive_loop(pid = Process.ppid)
- +:no_process_alive+ Process is not currently running
- +:no_home+ Daemon's home does not exist
Returns a symbol indicating the reason to stop:
Do not use directly, use +declare_alive+ instead.
By default, the parent process.
Loop checking if the process with PID +pid+ is still alive.
#
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
def default_options
#
def default_options { dir_mode: :normal, dir: daemon_home, multiple: false, log_output: true, stop_proc: :terminate } end
def in_loop
#
def in_loop if loop_i.nil? declare_alive daemon_first_loop @loop_i = -1 end @loop_i += 1 daemon_loop end
def last_alive
#
def last_alive self.class.last_alive(daemon_home) end
def launch_daemon_proc(options)
#
def launch_daemon_proc(options) Daemons.run_proc("#{daemon_name}", options) { while in_loop; end } end
def output_file
def output_file File.join(daemon_home, "#{daemon_name}.output") end
def pid_file
def pid_file File.join(daemon_home, "#{daemon_name}.pid") end
def process_alive?(pid)
Check if the process with PID +pid+ is still alive,
#
def process_alive?(pid) Process.kill(0, pid) true rescue Errno::ESRCH, Errno::EPERM, Errno::ENOENT terminate false end
def run(opts = [], wait = true)
#
def run(opts = [], wait = true) daemon(:run, opts, wait) end
def start(opts = [], wait = true)
#
def start(opts = [], wait = true) daemon(:start, opts, wait) end
def status(opts = [], wait = true)
#
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
def stop(opts = [], wait = true)
#
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
def terminate
#
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
def terminate_file
def terminate_file File.join(daemon_home, 'terminate-daemon') end
def terminated_file
def terminated_file self.class.terminated_file(daemon_home) end
def termination_file?(pid)
Check if a termination file exists and terminate process with PID +pid+
#
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
def write_alive_file
def write_alive_file File.open(alive_file, 'w') { |fh| fh.print Time.now.to_s } end