require'daemons'require'miga/common/with_daemon_class'### 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+moduleMiGA::Common::WithDaemon# Process ID of the forked process declaring the daemon aliveattr:declare_alive_pid# Loop counterattr:loop_idefpid_fileFile.join(daemon_home,"#{daemon_name}.pid")enddefoutput_fileFile.join(daemon_home,"#{daemon_name}.output")enddefterminate_fileFile.join(daemon_home,'terminate-daemon')enddefalive_fileself.class.alive_file(daemon_home)enddefterminated_fileself.class.terminated_file(daemon_home)end### When was the daemon last seen active?deflast_aliveself.class.last_alive(daemon_home)end### Is the daemon active?defactive?returnfalseunlessFile.exist?alive_file(last_alive||Time.new(0))>Time.now-60end### Tell the world that you're alive.defdeclare_aliveifactive?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 checkend### 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 filedefdeclare_alive_loop(pid=Process.ppid)i=-1loopdoi+=1return:no_homeunlessDir.exist?daemon_homereturn:no_process_aliveunlessprocess_alive?pidwrite_alive_fileifi%30==0return:termination_fileiftermination_file?pidsleep(1)endenddefwrite_alive_fileFile.open(alive_file,'w'){|fh|fh.printTime.now.to_s}end### Check if the process with PID +pid+ is still alive,# call +terminate+ otherwise.defprocess_alive?(pid)Process.kill(0,pid)truerescueErrno::ESRCH,Errno::EPERM,Errno::ENOENTterminatefalseend### Check if a termination file exists and terminate process with PID +pid+# if it does. Do not kill any process if +pid+ is +nil+deftermination_file?(pid)returnfalseunlessFile.exist?terminate_filesay'Found termination file, terminating'File.unlink(terminate_file)terminateProcess.kill(9,pid)unlesspid.nil?trueend### Returns Hash containing the default options for the daemon.defdefault_options{dir_mode: :normal,dir: daemon_home,multiple: false,log_output: true,stop_proc: :terminate}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.defdaemon(task,opts=[],wait=true)MiGA::MiGA.DEBUG"#{self.class}#daemon #{task}#{opts}"task=task.to_symraise"Unsupported task: #{task}"unlessrespond_to?taskreturnsend(task,opts,wait)unless%i[start run].include?task# start & run:options=default_optionsopts.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)ifwaitpidend### Stops the daemon with +opts+defstop(opts=[],wait=true)ifactive?say'Sending termination message'FileUtils.touch(terminate_file)sleep(0.5)whileactive?ifwaitFile.unlink(pid_file)ifFile.exist?(pid_file)elsesay'No running instances'endend### Returns the status of the daemon with +opts+defstatus(opts=[],wait=true)ifactive?say"Running with pid #{File.size?(pid_file)?File.read(pid_file):'?'}"elsesay'Not running'endend### Pass daemon options to +Daemons+. Do not use directly, use +daemon+ instead.deflaunch_daemon_proc(options)Daemons.run_proc("#{daemon_name}",options){whilein_loop;end}end### Initializes the daemon with +opts+defstart(opts=[],wait=true)daemon(:start,opts,wait)end### Initializes the daemon on top with +opts+defrun(opts=[],wait=true)daemon(:run,opts,wait)end### One loop, returns a boolean indicating if the execution should continuedefin_loopifloop_i.nil?declare_alivedaemon_first_loop@loop_i=-1end@loop_i+=1daemon_loopend### Declares a daemon termination. Do not use, directly, use #stop instead.defterminateunlessdeclare_alive_pid.nil?Process.kill(9,declare_alive_pid)@declare_alive_pid=nilendFile.rename(alive_file,terminated_file)ifFile.exist?alive_fileendend