forked from puppetlabs/puppet
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdaemon.rb
182 lines (147 loc) · 4.8 KB
/
daemon.rb
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
require 'puppet/application'
require 'puppet/scheduler'
# 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
class Puppet::Daemon
attr_accessor :agent, :server, :argv
def initialize(pidfile, scheduler = Puppet::Scheduler::Scheduler.new())
@scheduler = scheduler
@pidfile = pidfile
end
def daemonname
Puppet.run_mode.name
end
# Put the daemon into the background.
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
# 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
return unless agent
if agent.running?
Puppet.notice "Not triggering already-running agent"
return
end
agent.run({:splay => false})
end
def restart
Puppet::Application.restart!
reexec unless agent and agent.running?
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
signals = {:INT => :stop, :TERM => :stop }
# extended signals not supported under windows
signals.update({:HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs }) unless Puppet.features.microsoft_windows?
signals.each do |signal, method|
Signal.trap(signal) do
Puppet.notice "Caught #{signal}; calling #{method}"
send(method)
end
end
end
# Stop everything
def stop(args = {:exit => true})
Puppet::Application.stop!
server.stop if server
remove_pidfile
Puppet::Util::Log.close_all
exit if args[:exit]
end
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
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
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
reparse_run.disable if Puppet[:filetimeout] == 0
agent_run.disable unless agent
@scheduler.run_loop([reparse_run, agent_run])
end
end