Run periodic actions and a network server in a daemonized process.
A Daemon has 3 parts:
* config reparse * (optional) an agent that responds to #run * (optional) a server that response to #stop, #start, and #wait_for_shutdown
The config reparse will occur periodically based on Settings. The server will be started and is expected to manage its own run loop (and so not block the start call). The server will, however, still be waited for by using the wait_for_shutdown method. The agent is run periodically and a time interval based on Settings. The config reparse will update this time interval when needed.
The Daemon is also responsible for signal handling, starting, stopping, running the agent on demand, and reloading the entire process. It ensures that only one Daemon is running by using a lockfile.
@api private
Convenience signature for calling ::close_streams
# File lib/puppet/daemon.rb, line 77 def close_streams() Puppet::Daemon.close_streams end
Put the daemon into the background.
# File lib/puppet/daemon.rb, line 40 def daemonize if pid = fork Process.detach(pid) exit(0) end create_pidfile # Get rid of console logging Puppet::Util::Log.close(:console) Process.setsid Dir.chdir("/") close_streams end
# File lib/puppet/daemon.rb, line 35 def daemonname Puppet.run_mode.name end
# File lib/puppet/daemon.rb, line 81 def reexec raise Puppet::DevError, "Cannot reexec unless ARGV arguments are set" unless argv command = $0 + " " + argv.join(" ") Puppet.notice "Restarting with '#{command}'" stop(:exit => false) exec(command) end
# File lib/puppet/daemon.rb, line 89 def reload return unless agent if agent.running? Puppet.notice "Not triggering already-running agent" return end agent.run({:splay => false}) end
# File lib/puppet/daemon.rb, line 104 def reopen_logs Puppet::Util::Log.reopen end
# File lib/puppet/daemon.rb, line 99 def restart Puppet::Application.restart! reexec unless agent and agent.running? end
Trap a couple of the main signals. This should probably be handled in a way that anyone else can register callbacks for traps, but, eh.
# File lib/puppet/daemon.rb, line 110 def set_signal_traps [:INT, :TERM].each do |signal| Signal.trap(signal) do Puppet.notice "Caught #{signal}; exiting" stop end end # extended signals not supported under windows if !Puppet.features.microsoft_windows? signals = {:HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs } signals.each do |signal, method| Signal.trap(signal) do Puppet.notice "Caught #{signal}; storing #{method}" @signals << method end end end end
# File lib/puppet/daemon.rb, line 143 def start set_signal_traps create_pidfile raise Puppet::DevError, "Daemons must have an agent, server, or both" unless agent or server # Start the listening server, if required. server.start if server # Finally, loop forever running events - or, at least, until we exit. run_event_loop server.wait_for_shutdown if server end
Stop everything
# File lib/puppet/daemon.rb, line 131 def stop(args = {:exit => true}) Puppet::Application.stop! server.stop if server remove_pidfile Puppet::Util::Log.close_all exit if args[:exit] end
Close stdin/stdout/stderr so that we can finish our transition into ‘daemon’ mode. @return nil
# File lib/puppet/daemon.rb, line 59 def self.close_streams() Puppet.debug("Closing streams for daemon mode") begin $stdin.reopen "/dev/null" $stdout.reopen "/dev/null", "a" $stderr.reopen $stdout Puppet::Util::Log.reopen Puppet.debug("Finished closing streams for daemon mode") rescue => detail Puppet.err "Could not start #{Puppet.run_mode.name}: #{detail}" Puppet::Util::replace_file("/tmp/daemonout", 0644) do |f| f.puts "Could not start #{Puppet.run_mode.name}: #{detail}" end exit(12) end end
# File lib/puppet/daemon.rb, line 29 def initialize(pidfile, scheduler = Puppet::Scheduler::Scheduler.new()) @scheduler = scheduler @pidfile = pidfile @signals = [] end