Class: Emissary::Daemon
Constant Summary
collapse
- SHUTDOWN_RETRY =
1
- MAX_RESTARTS =
10
- REQUIRED_AGENTS =
[ :emissary, :ping, :error ]
ServerController::DEFAULT_PID_FILE_MODE, ServerController::SIGNALS
Instance Attribute Summary collapse
#pid_file, #pid_file_mode
Class Method Summary
collapse
Instance Method Summary
collapse
#alive?, #create_pid_file, #delete_pid_file, #pid, #pid_dir, #retrieve_pid, #trap_signals
Constructor Details
#initialize(name, opts = {}) ⇒ Daemon
Returns a new instance of Daemon.
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
# File 'lib/emissary/daemon.rb', line 140
def initialize(name, opts = {})
@operators = {}
@name = name
@mutex = Mutex.new
@shutdown = nil
self.class.const_set('STARTUP_OPTS', opts.clone_deep)
@config_file = File.expand_path(opts.delete(:config_file) || '/etc/emissary/config.ini')
@config = Daemon.get_config(config_file, STARTUP_OPTS)
self.class.const_set('CONFIG_FILE', @config_file)
@logger = Emissary::Logger.instance
@logger.level = @config[:general][:log_level]
@pid_file = config[:general][:pid_file]
@pid_file_mode = config[:general][:pid_file_mode] || DEFAULT_PID_FILE_MODE
ary = %w[name config_file].map { |var|
self.send(var.to_sym).nil? ? var : nil
}.compact
raise Error, "These variables are required: #{ary.join(', ')}." unless ary.empty?
end
|
Instance Attribute Details
Returns the value of attribute config.
137
138
139
|
# File 'lib/emissary/daemon.rb', line 137
def config
@config
end
|
#config_file ⇒ Object
Returns the value of attribute config_file.
137
138
139
|
# File 'lib/emissary/daemon.rb', line 137
def config_file
@config_file
end
|
Returns the value of attribute logger.
137
138
139
|
# File 'lib/emissary/daemon.rb', line 137
def logger
@logger
end
|
Returns the value of attribute name.
137
138
139
|
# File 'lib/emissary/daemon.rb', line 137
def name
@name
end
|
#operators ⇒ Object
Returns the value of attribute operators.
138
139
140
|
# File 'lib/emissary/daemon.rb', line 138
def operators
@operators
end
|
Returns the value of attribute state.
137
138
139
|
# File 'lib/emissary/daemon.rb', line 137
def state
@state
end
|
Class Method Details
.get_config(config_file, opts = {}) ⇒ Object
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
# File 'lib/emissary/daemon.rb', line 165
def self.get_config(config_file, opts = {})
config = Daemon.validate_config!(Emissary::ConfigFile.new(config_file))
config[:general][:daemonize] = opts.delete(:daemonize) || false
config[:general][:agents] ||= 'all'
config[:general][:agents] = if config[:general][:agents].instance_of? String
config[:general][:agents].split(/\s*,\s*/)
else
config[:general][:agents].to_a
end
config[:general][:log_level] = opts.delete(:log_level) || config[:general][:log_level] || 'NOTICE'
unless (log_level = config[:general][:log_level]).kind_of? Fixnum
case log_level
when /^(LOG_){0,1}([A-Z]+)$/i
log_level = Emissary::Logger::CONSTANT_NAME_MAP[$2]
when Symbol
log_level = Emissary::Logger::CONSTANT_NAME_MAP[log_level]
when /[0-9]+/
log_level = log_level.to_i
end
config[:general][:log_level] = log_level
end
config[:general][:pid_dir] = opts.delete(:pid_dir) || '/var/run'
config[:agents] ||= {}
config[:agents][:emissary] ||= {}
config[:agents][:emissary][:config_file] = File.expand_path(config_file)
config[:agents][:emissary][:config_path] = File.dirname(File.expand_path(config_file))
config[:general][:operators].each do |operator|
config[operator.to_sym].each do |name,data|
agents = data[:agents].blank? ? config[:general][:agents] : if data[:agents].kind_of?(Array)
data[:agents]
else
data[:agents].split(/\s*,\s*/)
end
disable = agents.select { |v| v =~ /^-/ }.inject([]) { |a,v| a << v.gsub(/^-/,'').to_sym }
disable.include?(:all) && disable.delete_if { |v| v != :all }
enable = agents.select { |v| v !~ /^-/ }.inject([]) { |a,v| a << v.to_sym; a }
enable.include?(:all) && enable.delete_if { |v| v != :all }
disable -= REQUIRED_AGENTS
enable = ( enable.include?(:all) ? [ :all ] : enable | REQUIRED_AGENTS )
if not (conflicts = (enable - (enable - disable))).empty?
raise "Conflicting enabled/disabled agents: [#{conflicts.join(', ')}] - you can not both specifically enable and disable an agent!"
end
data[:agents] = config[:agents].clone
data[:agents][:__enabled__] = enable
data[:agents][:__disabled__] = disable
end
end
config
end
|
.validate_config!(config) ⇒ Object
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
# File 'lib/emissary/daemon.rb', line 235
def self.validate_config!(config)
unless config[:general]
raise ::Emissary::ConfigValidationError.new(Exception, "Missing 'general' section in configuration file")
end
unless config[:general][:operators]
logger.debug config[:general].inspect
raise ::Emissary::ConfigValidationError.new(Exception, "[general][operators] not set")
end
unless config[:general][:operators].kind_of? Array
raise ::Emissary::ConfigValidationError.new(Exception, "[general][operators] not a list")
end
config[:general][:operators].each do |operator|
operator = operator.to_sym
unless config[operator]
raise ::Emissary::ConfigValidationError.new(Exception, "Missing Operator Section '#{operator}'")
end
unless config[operator].kind_of? Hash
raise ::Emissary::ConfigValidationError.new(Exception, "Operator Section '#{operator}' not a dictionary of operators")
end
end
config
end
|
Instance Method Details
#become_daemon ⇒ Object
263
264
265
266
267
268
|
# File 'lib/emissary/daemon.rb', line 263
def become_daemon
@logger.mode = Emissary::Logger::LOG_SYSLOG
Daemonize::daemonize(nil, name)
create_pid_file
end
|
#call_operators ⇒ Object
278
279
280
281
282
283
284
285
286
|
# File 'lib/emissary/daemon.rb', line 278
def call_operators
config[:general][:operators].each do |operator|
opsym = operator.to_sym
config[opsym].each do |name,data|
op = Emissary.call operator, data.merge({:signature => name, :parent_pid => $$})
@operators[op.signature] = { :operator => op, :start_count => 0 }
end
end
end
|
#can_startup?(operator) ⇒ Boolean
270
271
272
273
274
275
276
|
# File 'lib/emissary/daemon.rb', line 270
def can_startup? operator
result = true
result &= (!operator[:daemon] || !operator[:daemon].alive?)
result &= operator[:start_count] < MAX_RESTARTS
result &= (not @shutting_down)
result
end
|
#reconfig ⇒ Object
Also known as:
hup
288
289
290
291
292
293
294
295
296
297
298
|
# File 'lib/emissary/daemon.rb', line 288
def reconfig
Emissary.logger.warn "Reloading configuration."
begin
new_config = Daemon.get_config(config_file, STARTUP_OPTS)
rescue Exception => e
Emissary.logger.error "Unable to reload configuration due to error:\n#{e.message}\n\t#{e.backtrace.join("\n\t")}"
else
@config = new_config
end
self.restart
end
|
#restart ⇒ Object
Also known as:
usr1
300
301
302
303
|
# File 'lib/emissary/daemon.rb', line 300
def restart
shutdown false
startup
end
|
#shutdown(do_exit = true) ⇒ Object
Also known as:
int, term, kill, exit
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
|
# File 'lib/emissary/daemon.rb', line 320
def shutdown do_exit = true
Emissary.logger.info "Shutdown Requested - Stopping operators"
@operators.each_key do |name|
operator = do_exit ? @operators.delete(name) : @operators[name]
Emissary.logger.notice "Shutting down operator '#{name}' - current status: #{operator[:daemon].alive? ? 'running' : 'stopped'}"
while operator[:daemon].alive?
Emissary.logger.debug "[SHUTDOWN] Hanging Up on Operator call '#{name}' (process: #{operator[:daemon_pid]})"
operator[:daemon].shutdown if operator[:daemon].alive?
end
operator[:start_count] -= 1 unless operator[:start_count] <= 0
end
if do_exit
Emissary.logger.info "Shutdown Complete - Exiting..."
exit!(0)
end
end
|
#start_run_loop ⇒ Object
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
|
# File 'lib/emissary/daemon.rb', line 344
def start_run_loop
while not @shutting_down do
@operators.each do |name,operator|
if operators[:start_count].to_i > MAX_RESTARTS
::Emissary.logger.warning "Start Count > MAX_RESTARTS for operator '#{name}' - removing from list of operators..."
@operators.delete(name)
next
end
if can_startup? operator
Emissary.logger.notice "Starting up Operator: #{name}"
server_data = {
:operator => @operators[name][:operator],
:pid_file => File.join(pid_dir, "emop_#{name}"),
}
operator[:server] = Emissary::Server.new("emop_#{name}", server_data)
operator[:daemon] = Servolux::Daemon.new(:server => operator[:server])
if operator[:daemon].alive?
Emissary.logger.warning "Operator '#{name}' already running with pid '#{operator[:daemon].get_pid}'."
@operators.delete(name)
next
end
operator[:daemon].startup false
operator[:parent_pid] = retrieve_pid rescue $$
operator[:daemon_pid] = operator[:daemon].get_pid
operator[:start_count] += 1
if operator[:start_count] >= MAX_RESTARTS
Emissary.logger.warning "Operator '#{name}' has been restarted #{MAX_RESTARTS} times. " +
"I will not attempt to restart it anymore."
end
Emissary.logger.notice "Forked Operator '#{name}' with pid #{operator[:daemon_pid]}"
end
end
if @operators.length <= 0
Emissary.logger.notice "No operators left - shutting down."
shutdown true
end
sleep DAEMON_RECHECK_INTERVAL
end
end
|
305
306
307
308
309
310
311
312
313
314
315
316
317
318
|
# File 'lib/emissary/daemon.rb', line 305
def startup
return if alive?
begin
become_daemon if config[:general][:daemonize]
trap_signals
call_operators
start_run_loop
rescue StandardError => e
Emissary.logger.error "Error Starting up: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
ensure
delete_pid_file
end
end
|