emissary 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +203 -0
- data/README.txt +54 -0
- data/VERSION.yml +4 -0
- data/bin/emissary +196 -0
- data/bin/emissary-setup +75 -0
- data/etc/emissary/config.ini +13 -0
- data/etc/init.d/emissary +50 -0
- data/lib/emissary.rb +223 -0
- data/lib/emissary/agent.rb +61 -0
- data/lib/emissary/agent/emissary.rb +163 -0
- data/lib/emissary/agent/error.rb +26 -0
- data/lib/emissary/agent/file.rb +26 -0
- data/lib/emissary/agent/gem.rb +42 -0
- data/lib/emissary/agent/mysql.rb +219 -0
- data/lib/emissary/agent/ping.rb +37 -0
- data/lib/emissary/agent/proxy.rb +26 -0
- data/lib/emissary/agent/rabbitmq.rb +233 -0
- data/lib/emissary/agent/sshkeys.rb +152 -0
- data/lib/emissary/agent/stats.rb +96 -0
- data/lib/emissary/agent/test.rb +40 -0
- data/lib/emissary/config.rb +231 -0
- data/lib/emissary/core_ext/blank.rb +60 -0
- data/lib/emissary/core_ext/misc_object.rb +21 -0
- data/lib/emissary/core_ext/symbolize.rb +33 -0
- data/lib/emissary/daemon.rb +404 -0
- data/lib/emissary/errors.rb +106 -0
- data/lib/emissary/gem_helper.rb +183 -0
- data/lib/emissary/identity.rb +183 -0
- data/lib/emissary/identity/ec2.rb +64 -0
- data/lib/emissary/identity/unix.rb +67 -0
- data/lib/emissary/logger.rb +130 -0
- data/lib/emissary/message.rb +217 -0
- data/lib/emissary/operator.rb +274 -0
- data/lib/emissary/operator/amqp.rb +203 -0
- data/lib/emissary/server.rb +98 -0
- data/lib/emissary/servolux.rb +75 -0
- metadata +262 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
##### BORROWED FROM ACTIVESUPPORT #####
|
2
|
+
|
3
|
+
class Object
|
4
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
5
|
+
# For example, "", " ", +nil+, [], and {} are blank.
|
6
|
+
#
|
7
|
+
# This simplifies
|
8
|
+
#
|
9
|
+
# if !address.nil? && !address.empty?
|
10
|
+
#
|
11
|
+
# to
|
12
|
+
#
|
13
|
+
# if !address.blank?
|
14
|
+
def blank?
|
15
|
+
respond_to?(:empty?) ? empty? : !self
|
16
|
+
end
|
17
|
+
|
18
|
+
# An object is present if it's not blank.
|
19
|
+
def present?
|
20
|
+
!blank?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class NilClass #:nodoc:
|
25
|
+
def blank?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class FalseClass #:nodoc:
|
31
|
+
def blank?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class TrueClass #:nodoc:
|
37
|
+
def blank?
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Array #:nodoc:
|
43
|
+
alias_method :blank?, :empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
class Hash #:nodoc:
|
47
|
+
alias_method :blank?, :empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
class String #:nodoc:
|
51
|
+
def blank?
|
52
|
+
self !~ /\S/
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Numeric #:nodoc:
|
57
|
+
def blank?
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
##### BORROWED FROM ACTIVESUPPORT #####
|
2
|
+
|
3
|
+
class Object
|
4
|
+
def try name, *args
|
5
|
+
self.__send__(name, *args) unless not self.respond_to? name
|
6
|
+
end
|
7
|
+
|
8
|
+
def __method__
|
9
|
+
caller[0] =~ /\d:in `([^']+)'/
|
10
|
+
$1.to_sym rescue nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def __caller__
|
14
|
+
caller[1] =~ /\d:in `([^']+)'/
|
15
|
+
$1.to_sym rescue nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def clone_deep
|
19
|
+
Marshal.load(Marshal.dump(self)) rescue self.clone
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Array
|
2
|
+
def symbolize
|
3
|
+
collect do |value|
|
4
|
+
case value
|
5
|
+
when Hash, Array
|
6
|
+
value.symbolize
|
7
|
+
else
|
8
|
+
value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def symbolize!
|
14
|
+
self.replace(self.symbolize)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Hash
|
19
|
+
def symbolize
|
20
|
+
inject({}) do |hash,(key,value)|
|
21
|
+
hash[(key.to_sym rescue key) || key] = case value
|
22
|
+
when Hash, Array
|
23
|
+
value.symbolize
|
24
|
+
else
|
25
|
+
value
|
26
|
+
end
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
end
|
30
|
+
def symbolize!
|
31
|
+
self.replace(self.symbolize)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,404 @@
|
|
1
|
+
# Copyright 2010 The New York Times
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
require 'emissary/servolux'
|
17
|
+
require 'emissary/server'
|
18
|
+
require 'daemons'
|
19
|
+
|
20
|
+
module Emissary
|
21
|
+
|
22
|
+
# Some of the ServerController/Daemon stuff has been borrowed
|
23
|
+
# from Servolux::Daemon and Servolux::Server, so:
|
24
|
+
# Thanks to Tim Pease for those parts that are gleaned from Servolux
|
25
|
+
module ServerController
|
26
|
+
|
27
|
+
SIGNALS = %w[HUP INT TERM USR1 USR2 EXIT] & Signal.list.keys
|
28
|
+
SIGNALS.each {|sig| sig.freeze}.freeze
|
29
|
+
|
30
|
+
DEFAULT_PID_FILE_MODE = 0640
|
31
|
+
|
32
|
+
attr_accessor :pid_file_mode
|
33
|
+
attr_writer :pid_file
|
34
|
+
|
35
|
+
# Returns +true+ if the daemon process is currently running. Returns
|
36
|
+
# +false+ if this is not the case. The status of the process is determined
|
37
|
+
# by sending a signal to the process identified by the +pid_file+.
|
38
|
+
#
|
39
|
+
# @return [Boolean]
|
40
|
+
#
|
41
|
+
def alive?
|
42
|
+
pid = retrieve_pid
|
43
|
+
Process.kill(0, pid)
|
44
|
+
true
|
45
|
+
rescue Errno::ESRCH, Errno::ENOENT
|
46
|
+
false
|
47
|
+
rescue Errno::EACCES => err
|
48
|
+
logger.error "You do not have access to the PID file at " \
|
49
|
+
"#{pid_file.inspect}: #{err.message}"
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
def retrieve_pid
|
54
|
+
Integer(File.read(pid_file).strip)
|
55
|
+
rescue TypeError
|
56
|
+
raise Error, "A PID file was not specified."
|
57
|
+
rescue ArgumentError
|
58
|
+
raise Error, "#{pid_file.inspect} does not contain a valid PID."
|
59
|
+
end
|
60
|
+
|
61
|
+
# Send a signal to the daemon process identified by the PID file. The
|
62
|
+
# default signal to send is 'INT' (2). The signal can be given either as a
|
63
|
+
# string or a signal number.
|
64
|
+
#
|
65
|
+
# @param [String, Integer] signal The kill signal to send to the daemon
|
66
|
+
# process
|
67
|
+
# @return [Daemon] self
|
68
|
+
#
|
69
|
+
def kill( signal = 'INT' )
|
70
|
+
signal = Signal.list.invert[signal] if signal.is_a?(Integer)
|
71
|
+
pid = retrieve_pid
|
72
|
+
logger.info "Killing PID #{pid} with #{signal}"
|
73
|
+
Process.kill(signal, pid)
|
74
|
+
self
|
75
|
+
rescue Errno::EINVAL
|
76
|
+
logger.error "Failed to kill PID #{pid} with #{signal}: " \
|
77
|
+
"'#{signal}' is an invalid or unsupported signal number."
|
78
|
+
rescue Errno::EPERM
|
79
|
+
logger.error "Failed to kill PID #{pid} with #{signal}: " \
|
80
|
+
"Insufficient permissions."
|
81
|
+
rescue Errno::ESRCH
|
82
|
+
logger.error "Failed to kill PID #{pid} with #{signal}: " \
|
83
|
+
"Process is deceased or zombie."
|
84
|
+
rescue Errno::EACCES => err
|
85
|
+
logger.error err.message
|
86
|
+
rescue Errno::ENOENT => err
|
87
|
+
logger.error "Could not find a PID file at #{pid_file.inspect}. " \
|
88
|
+
"Most likely the process is no longer running."
|
89
|
+
rescue Exception => err
|
90
|
+
unless err.is_a?(SystemExit)
|
91
|
+
logger.error "Failed to kill PID #{pid} with #{signal}: #{err.message}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def pid
|
96
|
+
alive? ? retrieve_pid : nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def pid_file
|
100
|
+
@pid_file ||= File.join(config[:general][:pid_dir], (config[:general][:pid_file] || 'emissary.pid'))
|
101
|
+
end
|
102
|
+
|
103
|
+
def pid_dir
|
104
|
+
@pid_dir ||= File.join(config[:general][:pid_dir])
|
105
|
+
end
|
106
|
+
|
107
|
+
def create_pid_file
|
108
|
+
logger.debug "Server #{name.inspect} creating pid file #{pid_file.inspect}"
|
109
|
+
File.open(pid_file, 'w', pid_file_mode) {|fd| fd.write(Process.pid.to_s)}
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete_pid_file
|
113
|
+
if test(?f, pid_file)
|
114
|
+
pid = Integer(File.read(pid_file).strip)
|
115
|
+
return unless pid == Process.pid
|
116
|
+
|
117
|
+
logger.debug "Server #{name.inspect} removing pid file #{pid_file.inspect}"
|
118
|
+
File.delete(pid_file)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def trap_signals
|
123
|
+
SIGNALS.each do |sig|
|
124
|
+
m = sig.downcase.to_sym
|
125
|
+
Signal.trap(sig) { self.send(m) rescue nil } if self.respond_to? m
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class Daemon
|
131
|
+
include Emissary::ServerController
|
132
|
+
|
133
|
+
SHUTDOWN_RETRY = 1
|
134
|
+
MAX_RESTARTS = 10
|
135
|
+
REQUIRED_AGENTS = [ :emissary, :ping, :error ]
|
136
|
+
|
137
|
+
attr_accessor :logger, :state, :name, :config, :config_file
|
138
|
+
attr_reader :operators
|
139
|
+
|
140
|
+
def initialize(name, opts = {})
|
141
|
+
|
142
|
+
@operators = {}
|
143
|
+
@name = name
|
144
|
+
@mutex = Mutex.new
|
145
|
+
@shutdown = nil
|
146
|
+
|
147
|
+
self.class.const_set('STARTUP_OPTS', opts.clone_deep)
|
148
|
+
@config_file = File.expand_path(opts.delete(:config_file) || '/etc/emissary/config.ini')
|
149
|
+
@config = Daemon.get_config(config_file, STARTUP_OPTS)
|
150
|
+
|
151
|
+
self.class.const_set('CONFIG_FILE', @config_file)
|
152
|
+
|
153
|
+
@logger = Emissary::Logger.instance
|
154
|
+
@logger.level = @config[:general][:log_level]
|
155
|
+
|
156
|
+
@pid_file = config[:general][:pid_file]
|
157
|
+
@pid_file_mode = config[:general][:pid_file_mode] || DEFAULT_PID_FILE_MODE
|
158
|
+
|
159
|
+
ary = %w[name config_file].map { |var|
|
160
|
+
self.send(var.to_sym).nil? ? var : nil
|
161
|
+
}.compact
|
162
|
+
raise Error, "These variables are required: #{ary.join(', ')}." unless ary.empty?
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.get_config(config_file, opts = {})
|
166
|
+
config = Daemon.validate_config!(Emissary::ConfigFile.new(config_file))
|
167
|
+
|
168
|
+
config[:general][:daemonize] = opts.delete(:daemonize) || false
|
169
|
+
|
170
|
+
config[:general][:agents] ||= 'all'
|
171
|
+
config[:general][:agents] = if config[:general][:agents].instance_of? String
|
172
|
+
config[:general][:agents].split(/\s*,\s*/)
|
173
|
+
else
|
174
|
+
config[:general][:agents].to_a
|
175
|
+
end
|
176
|
+
|
177
|
+
config[:general][:log_level] = opts.delete(:log_level) || config[:general][:log_level] || 'NOTICE'
|
178
|
+
|
179
|
+
unless (log_level = config[:general][:log_level]).kind_of? Fixnum
|
180
|
+
case log_level
|
181
|
+
when /^(LOG_){0,1}([A-Z]+)$/i
|
182
|
+
log_level = Emissary::Logger::CONSTANT_NAME_MAP[$2]
|
183
|
+
when Symbol
|
184
|
+
log_level = Emissary::Logger::CONSTANT_NAME_MAP[log_level]
|
185
|
+
when /[0-9]+/
|
186
|
+
log_level = log_level.to_i
|
187
|
+
end
|
188
|
+
config[:general][:log_level] = log_level
|
189
|
+
end
|
190
|
+
|
191
|
+
config[:general][:pid_dir] = opts.delete(:pid_dir) || '/var/run'
|
192
|
+
|
193
|
+
# set up defaults
|
194
|
+
config[:agents] ||= {}
|
195
|
+
config[:agents][:emissary] ||= {}
|
196
|
+
config[:agents][:emissary][:config_file] = File.expand_path(config_file)
|
197
|
+
config[:agents][:emissary][:config_path] = File.dirname(File.expand_path(config_file))
|
198
|
+
|
199
|
+
|
200
|
+
config[:general][:operators].each do |operator|
|
201
|
+
config[operator.to_sym].each do |name,data|
|
202
|
+
# setup the enabled and disabled agents on a per operator basis
|
203
|
+
agents = data[:agents].blank? ? config[:general][:agents] : if data[:agents].kind_of?(Array)
|
204
|
+
data[:agents]
|
205
|
+
else
|
206
|
+
data[:agents].split(/\s*,\s*/)
|
207
|
+
end
|
208
|
+
|
209
|
+
disable = agents.select { |v| v =~ /^-/ }.inject([]) { |a,v| a << v.gsub(/^-/,'').to_sym }
|
210
|
+
disable.include?(:all) && disable.delete_if { |v| v != :all }
|
211
|
+
|
212
|
+
enable = agents.select { |v| v !~ /^-/ }.inject([]) { |a,v| a << v.to_sym; a }
|
213
|
+
enable.include?(:all) && enable.delete_if { |v| v != :all }
|
214
|
+
|
215
|
+
# don't let the user disable REQUIRED AGENTS
|
216
|
+
disable -= REQUIRED_AGENTS
|
217
|
+
|
218
|
+
enable = ( enable.include?(:all) ? [ :all ] : enable | REQUIRED_AGENTS )
|
219
|
+
|
220
|
+
if not (conflicts = (enable - (enable - disable))).empty?
|
221
|
+
raise "Conflicting enabled/disabled agents: [#{conflicts.join(', ')}] - you can not both specifically enable and disable an agent!"
|
222
|
+
end
|
223
|
+
|
224
|
+
# now copy over the agent specific settings and
|
225
|
+
# append __enabled__ and __disabled__ list
|
226
|
+
data[:agents] = config[:agents].clone
|
227
|
+
data[:agents][:__enabled__] = enable
|
228
|
+
data[:agents][:__disabled__] = disable
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
config
|
233
|
+
end
|
234
|
+
|
235
|
+
def self.validate_config!(config)
|
236
|
+
unless config[:general]
|
237
|
+
raise ::Emissary::ConfigValidationError.new(Exception, "Missing 'general' section in configuration file")
|
238
|
+
end
|
239
|
+
|
240
|
+
unless config[:general][:operators]
|
241
|
+
logger.debug config[:general].inspect
|
242
|
+
raise ::Emissary::ConfigValidationError.new(Exception, "[general][operators] not set")
|
243
|
+
end
|
244
|
+
|
245
|
+
unless config[:general][:operators].kind_of? Array
|
246
|
+
raise ::Emissary::ConfigValidationError.new(Exception, "[general][operators] not a list")
|
247
|
+
end
|
248
|
+
|
249
|
+
config[:general][:operators].each do |operator|
|
250
|
+
operator = operator.to_sym
|
251
|
+
unless config[operator]
|
252
|
+
raise ::Emissary::ConfigValidationError.new(Exception, "Missing Operator Section '#{operator}'")
|
253
|
+
end
|
254
|
+
|
255
|
+
unless config[operator].kind_of? Hash
|
256
|
+
raise ::Emissary::ConfigValidationError.new(Exception, "Operator Section '#{operator}' not a dictionary of operators")
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
config
|
261
|
+
end
|
262
|
+
|
263
|
+
def become_daemon
|
264
|
+
# switch to syslog mode for logging
|
265
|
+
@logger.mode = Emissary::Logger::LOG_SYSLOG
|
266
|
+
Daemonize::daemonize(nil, name)
|
267
|
+
create_pid_file
|
268
|
+
end
|
269
|
+
|
270
|
+
def can_startup? operator
|
271
|
+
result = true
|
272
|
+
result &= (!operator[:daemon] || !operator[:daemon].alive?)
|
273
|
+
result &= operator[:start_count] < MAX_RESTARTS
|
274
|
+
result &= (not @shutting_down)
|
275
|
+
result
|
276
|
+
end
|
277
|
+
|
278
|
+
def call_operators
|
279
|
+
config[:general][:operators].each do |operator|
|
280
|
+
opsym = operator.to_sym
|
281
|
+
config[opsym].each do |name,data|
|
282
|
+
op = Emissary.call operator, data.merge({:signature => name, :parent_pid => $$})
|
283
|
+
@operators[op.signature] = { :operator => op, :start_count => 0 }
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def reconfig
|
289
|
+
Emissary.logger.warn "Reloading configuration."
|
290
|
+
begin
|
291
|
+
new_config = Daemon.get_config(config_file, STARTUP_OPTS)
|
292
|
+
rescue Exception => e
|
293
|
+
Emissary.logger.error "Unable to reload configuration due to error:\n#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
294
|
+
else
|
295
|
+
@config = new_config
|
296
|
+
end
|
297
|
+
self.restart
|
298
|
+
end
|
299
|
+
|
300
|
+
def restart
|
301
|
+
shutdown false
|
302
|
+
startup
|
303
|
+
end
|
304
|
+
|
305
|
+
def startup
|
306
|
+
return if alive?
|
307
|
+
|
308
|
+
begin
|
309
|
+
become_daemon if config[:general][:daemonize]
|
310
|
+
trap_signals
|
311
|
+
call_operators
|
312
|
+
start_run_loop
|
313
|
+
rescue StandardError => e
|
314
|
+
Emissary.logger.error "Error Starting up: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
315
|
+
ensure
|
316
|
+
delete_pid_file
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def shutdown do_exit = true
|
321
|
+
Emissary.logger.info "Shutdown Requested - Stopping operators"
|
322
|
+
|
323
|
+
@operators.each_key do |name|
|
324
|
+
operator = do_exit ? @operators.delete(name) : @operators[name]
|
325
|
+
|
326
|
+
Emissary.logger.notice "Shutting down operator '#{name}' - current status: #{operator[:daemon].alive? ? 'running' : 'stopped'}"
|
327
|
+
while operator[:daemon].alive?
|
328
|
+
Emissary.logger.debug "[SHUTDOWN] Hanging Up on Operator call '#{name}' (process: #{operator[:daemon_pid]})"
|
329
|
+
# should have shutdown above but, let's be sure here
|
330
|
+
operator[:daemon].shutdown if operator[:daemon].alive?
|
331
|
+
end
|
332
|
+
|
333
|
+
# Our shutdowns don't count toward restart limit for operators
|
334
|
+
# We're only protecting against multiple failed starts with it.
|
335
|
+
operator[:start_count] -= 1 unless operator[:start_count] <= 0
|
336
|
+
end
|
337
|
+
|
338
|
+
if do_exit
|
339
|
+
Emissary.logger.info "Shutdown Complete - Exiting..."
|
340
|
+
exit!(0)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def start_run_loop
|
345
|
+
while not @shutting_down do
|
346
|
+
@operators.each do |name,operator|
|
347
|
+
if operators[:start_count].to_i > MAX_RESTARTS
|
348
|
+
::Emissary.logger.warning "Start Count > MAX_RESTARTS for operator '#{name}' - removing from list of operators..."
|
349
|
+
@operators.delete(name)
|
350
|
+
next
|
351
|
+
end
|
352
|
+
|
353
|
+
if can_startup? operator
|
354
|
+
Emissary.logger.notice "Starting up Operator: #{name}"
|
355
|
+
|
356
|
+
server_data = {
|
357
|
+
:operator => @operators[name][:operator],
|
358
|
+
:pid_file => File.join(pid_dir, "emop_#{name}"),
|
359
|
+
}
|
360
|
+
|
361
|
+
operator[:server] = Emissary::Server.new("emop_#{name}", server_data)
|
362
|
+
operator[:daemon] = Servolux::Daemon.new(:server => operator[:server])
|
363
|
+
|
364
|
+
# if the daemon is already alive before we've called startup
|
365
|
+
# then some other process started it, so we don't bother
|
366
|
+
if operator[:daemon].alive?
|
367
|
+
Emissary.logger.warning "Operator '#{name}' already running with pid '#{operator[:daemon].get_pid}'."
|
368
|
+
@operators.delete(name)
|
369
|
+
next
|
370
|
+
end
|
371
|
+
|
372
|
+
operator[:daemon].startup false
|
373
|
+
operator[:parent_pid] = retrieve_pid rescue $$
|
374
|
+
operator[:daemon_pid] = operator[:daemon].get_pid
|
375
|
+
operator[:start_count] += 1
|
376
|
+
|
377
|
+
if operator[:start_count] >= MAX_RESTARTS
|
378
|
+
Emissary.logger.warning "Operator '#{name}' has been restarted #{MAX_RESTARTS} times. " +
|
379
|
+
"I will not attempt to restart it anymore."
|
380
|
+
end
|
381
|
+
|
382
|
+
Emissary.logger.notice "Forked Operator '#{name}' with pid #{operator[:daemon_pid]}"
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# if there are no operators left, then there is no point
|
387
|
+
# continue - so exit...
|
388
|
+
if @operators.length <= 0
|
389
|
+
Emissary.logger.notice "No operators left - shutting down."
|
390
|
+
shutdown true
|
391
|
+
end
|
392
|
+
|
393
|
+
sleep DAEMON_RECHECK_INTERVAL
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
alias :int :shutdown # handles the INT signal
|
398
|
+
alias :term :shutdown # handles the TERM signal
|
399
|
+
alias :kill :shutdown # handles the KILL signal
|
400
|
+
alias :exit :shutdown # handles the EXIT signal
|
401
|
+
alias :hup :reconfig # handles the HUP signal
|
402
|
+
alias :usr1 :restart # handles the USR1 signal
|
403
|
+
end
|
404
|
+
end
|