class Puppet::Daemon

Run periodic actions in a daemonized process.

A Daemon has 2 parts:

* config reparse
* an agent that responds to #run

The config reparse will occur periodically based on Settings. 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

Constants

SIGNAL_CHECK_INTERVAL

Attributes

agent[R]
argv[RW]
signals[R]

Public Class Methods

close_streams() click to toggle source

Close stdin/stdout/stderr so that we can finish our transition into 'daemon' mode. @return nil

   # File lib/puppet/daemon.rb
58 def self.close_streams()
59   Puppet.debug("Closing streams for daemon mode")
60   begin
61     $stdin.reopen "/dev/null"
62     $stdout.reopen "/dev/null", "a"
63     $stderr.reopen $stdout
64     Puppet::Util::Log.reopen
65     Puppet.debug("Finished closing streams for daemon mode")
66   rescue => detail
67     Puppet.err "Could not start #{Puppet.run_mode.name}: #{detail}"
68     Puppet::Util::replace_file("/tmp/daemonout", 0644) do |f|
69       f.puts "Could not start #{Puppet.run_mode.name}: #{detail}"
70     end
71     exit(12)
72   end
73 end
new(agent, pidfile, scheduler = Puppet::Scheduler::Scheduler.new()) click to toggle source
   # File lib/puppet/daemon.rb
25 def initialize(agent, pidfile, scheduler = Puppet::Scheduler::Scheduler.new())
26   raise Puppet::DevError, _("Daemons must have an agent") unless agent
27   @scheduler = scheduler
28   @pidfile = pidfile
29   @agent = agent
30   @signals = []
31 end

Public Instance Methods

close_streams() click to toggle source

Convenience signature for calling Puppet::Daemon.close_streams

   # File lib/puppet/daemon.rb
76 def close_streams()
77   Puppet::Daemon.close_streams
78 end
daemonize() click to toggle source

Put the daemon into the background.

   # File lib/puppet/daemon.rb
38 def daemonize
39   pid = fork
40   if pid
41     Process.detach(pid)
42     exit(0)
43   end
44 
45   create_pidfile
46 
47   # Get rid of console logging
48   Puppet::Util::Log.close(:console)
49 
50   Process.setsid
51   Dir.chdir("/")
52 
53   close_streams
54 end
daemonname() click to toggle source
   # File lib/puppet/daemon.rb
33 def daemonname
34   Puppet.run_mode.name
35 end
reexec() click to toggle source
   # File lib/puppet/daemon.rb
80 def reexec
81   raise Puppet::DevError, _("Cannot reexec unless ARGV arguments are set") unless argv
82   command = $0 + " " + argv.join(" ")
83   Puppet.notice "Restarting with '#{command}'"
84   stop(:exit => false)
85   exec(command)
86 end
reload() click to toggle source
   # File lib/puppet/daemon.rb
88 def reload
89   agent.run({:splay => false})
90 rescue Puppet::LockError
91   Puppet.notice "Not triggering already-running agent"
92 end
reopen_logs() click to toggle source
    # File lib/puppet/daemon.rb
 99 def reopen_logs
100   Puppet::Util::Log.reopen
101 end
restart() click to toggle source
   # File lib/puppet/daemon.rb
94 def restart
95   Puppet::Application.restart!
96   reexec
97 end
set_signal_traps() click to toggle source

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
105 def set_signal_traps
106   [:INT, :TERM].each do |signal|
107     Signal.trap(signal) do
108       Puppet.notice "Caught #{signal}; exiting"
109       stop
110     end
111   end
112 
113   # extended signals not supported under windows
114   if !Puppet::Util::Platform.windows?
115     signals = {:HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs }
116     signals.each do |signal, method|
117       Signal.trap(signal) do
118         Puppet.notice "Caught #{signal}; storing #{method}"
119         @signals << method
120       end
121     end
122   end
123 end
start() click to toggle source
    # File lib/puppet/daemon.rb
136 def start
137   create_pidfile
138   run_event_loop
139 end
stop(args = {:exit => true}) click to toggle source

Stop everything

    # File lib/puppet/daemon.rb
126 def stop(args = {:exit => true})
127   Puppet::Application.stop!
128 
129   remove_pidfile
130 
131   Puppet::Util::Log.close_all
132 
133   exit if args[:exit]
134 end

Private Instance Methods

create_pidfile() click to toggle source

Create a pidfile for our daemon, so we can be stopped and others don't try to start.

    # File lib/puppet/daemon.rb
145 def create_pidfile
146   raise "Could not create PID file: #{@pidfile.file_path}" unless @pidfile.lock
147 end
remove_pidfile() click to toggle source

Remove the pid file for our daemon.

    # File lib/puppet/daemon.rb
150 def remove_pidfile
151   @pidfile.unlock
152 end
run_event_loop() click to toggle source

Loop forever running events - or, at least, until we exit.

    # File lib/puppet/daemon.rb
155 def run_event_loop
156   agent_run = Puppet::Scheduler.create_job(Puppet[:runinterval], Puppet[:splay], Puppet[:splaylimit]) do
157     # Splay for the daemon is handled in the scheduler
158     agent.run(:splay => false)
159   end
160 
161   reparse_run = Puppet::Scheduler.create_job(Puppet[:filetimeout]) do
162     Puppet.settings.reparse_config_files
163     agent_run.run_interval = Puppet[:runinterval]
164     if Puppet[:filetimeout] == 0
165       reparse_run.disable
166     else
167       reparse_run.run_interval = Puppet[:filetimeout]
168     end
169   end
170 
171   signal_loop = Puppet::Scheduler.create_job(SIGNAL_CHECK_INTERVAL) do
172     while method = @signals.shift #rubocop:disable Lint/AssignmentInCondition
173       Puppet.notice "Processing #{method}"
174       send(method)
175     end
176   end
177 
178   reparse_run.disable if Puppet[:filetimeout] == 0
179 
180   @scheduler.run_loop([reparse_run, agent_run, signal_loop])
181 end