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

Public Instance Methods

close_streams() click to toggle source

Convenience signature for calling Puppet::Daemon.close_streams

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

Put the daemon into the background.

   # File lib/puppet/daemon.rb
39 def daemonize
40   pid = fork
41   if pid
42     Process.detach(pid)
43     exit(0)
44   end
45 
46   create_pidfile
47 
48   # Get rid of console logging
49   Puppet::Util::Log.close(:console)
50 
51   Process.setsid
52   Dir.chdir("/")
53 
54   close_streams
55 end
daemonname() click to toggle source
   # File lib/puppet/daemon.rb
34 def daemonname
35   Puppet.run_mode.name
36 end
reexec() click to toggle source
   # File lib/puppet/daemon.rb
81 def reexec
82   raise Puppet::DevError, _("Cannot reexec unless ARGV arguments are set") unless argv
83   command = $0 + " " + argv.join(" ")
84   Puppet.notice "Restarting with '#{command}'"
85   stop(:exit => false)
86   exec(command)
87 end
reload() click to toggle source
   # File lib/puppet/daemon.rb
89 def reload
90   agent.run({:splay => false})
91 rescue Puppet::LockError
92   Puppet.notice "Not triggering already-running agent"
93 end
reopen_logs() click to toggle source
    # File lib/puppet/daemon.rb
100 def reopen_logs
101   Puppet::Util::Log.reopen
102 end
restart() click to toggle source
   # File lib/puppet/daemon.rb
95 def restart
96   Puppet::Application.restart!
97   reexec
98 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
106 def set_signal_traps
107   [:INT, :TERM].each do |signal|
108     Signal.trap(signal) do
109       Puppet.notice "Caught #{signal}; exiting"
110       stop
111     end
112   end
113 
114   # extended signals not supported under windows
115   if !Puppet::Util::Platform.windows?
116     signals = {:HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs }
117     signals.each do |signal, method|
118       Signal.trap(signal) do
119         Puppet.notice "Caught #{signal}; storing #{method}"
120         @signals << method
121       end
122     end
123   end
124 end
start() click to toggle source
    # File lib/puppet/daemon.rb
137 def start
138   create_pidfile
139   run_event_loop
140 end
stop(args = {:exit => true}) click to toggle source

Stop everything

    # File lib/puppet/daemon.rb
127 def stop(args = {:exit => true})
128   Puppet::Application.stop!
129 
130   remove_pidfile
131 
132   Puppet::Util::Log.close_all
133 
134   exit if args[:exit]
135 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
146 def create_pidfile
147   raise "Could not create PID file: #{@pidfile.file_path}" unless @pidfile.lock
148 end
remove_pidfile() click to toggle source

Remove the pid file for our daemon.

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

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

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