1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
require_relative '../puppet/application'
require_relative '../puppet/scheduler'
# 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
class Puppet::Daemon
SIGNAL_CHECK_INTERVAL = 5
attr_accessor :argv
attr_reader :signals, :agent
def initialize(agent, pidfile, scheduler = Puppet::Scheduler::Scheduler.new())
raise Puppet::DevError, _("Daemons must have an agent") unless agent
@scheduler = scheduler
@pidfile = pidfile
@agent = agent
@signals = []
end
def daemonname
Puppet.run_mode.name
end
# Put the daemon into the background.
def daemonize
pid = fork
if pid
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
# Close stdin/stdout/stderr so that we can finish our transition into 'daemon' mode.
# @return nil
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
# Convenience signature for calling Puppet::Daemon.close_streams
def close_streams()
Puppet::Daemon.close_streams
end
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
def reload
agent.run({:splay => false})
rescue Puppet::LockError
Puppet.notice "Not triggering already-running agent"
end
def restart
Puppet::Application.restart!
reexec
end
def reopen_logs
Puppet::Util::Log.reopen
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.
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::Util::Platform.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
# Stop everything
def stop(args = {:exit => true})
Puppet::Application.stop!
remove_pidfile
Puppet::Util::Log.close_all
exit if args[:exit]
end
def start
create_pidfile
run_event_loop
end
private
# Create a pidfile for our daemon, so we can be stopped and others
# don't try to start.
def create_pidfile
raise "Could not create PID file: #{@pidfile.file_path}" unless @pidfile.lock
end
# Remove the pid file for our daemon.
def remove_pidfile
@pidfile.unlock
end
# Loop forever running events - or, at least, until we exit.
def run_event_loop
agent_run = Puppet::Scheduler.create_job(Puppet[:runinterval], Puppet[:splay], Puppet[:splaylimit]) do
# Splay for the daemon is handled in the scheduler
agent.run(:splay => false)
end
reparse_run = Puppet::Scheduler.create_job(Puppet[:filetimeout]) do
Puppet.settings.reparse_config_files
agent_run.run_interval = Puppet[:runinterval]
if Puppet[:filetimeout] == 0
reparse_run.disable
else
reparse_run.run_interval = Puppet[:filetimeout]
end
end
signal_loop = Puppet::Scheduler.create_job(SIGNAL_CHECK_INTERVAL) do
while method = @signals.shift #rubocop:disable Lint/AssignmentInCondition
Puppet.notice "Processing #{method}"
send(method)
end
end
reparse_run.disable if Puppet[:filetimeout] == 0
@scheduler.run_loop([reparse_run, agent_run, signal_loop])
end
end
|