Class: Zmb

Inherits:
Object
  • Object
show all
Defined in:
lib/zmb.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_dir) ⇒ Zmb

Returns a new instance of Zmb.



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
# File 'lib/zmb.rb', line 27

def initialize(config_dir)
  @debug = false
  @running = false
  
  @plugin_manager = PluginManager.new(self)
  @settings_manager = Settings.new(config_dir)
  
  @instances = {'zmb' => self}
  @sockets = Hash.new
  
  @minimum_timeout = 0.5 # Half a second
  @maximum_timeout = 60.0 # Sixty seconds
  @timers = Array.new
  timer_add(Timer.new(self, :save, 120.0, true)) # Save every 2 minutes
  
  plugin_dir = File.join(@settings_manager.directory, 'plugins')
  if not File.exist?(plugin_dir) then
    FileUtils.makedirs(plugin_dir)
  end
  
  @plugin_sources = @settings_manager.get('zmb', 'plugin_sources', [])
  @plugin_sources.each{ |source| @plugin_manager.add_plugin_source source }
  @plugin_manager.add_plugin_source File.join(File.expand_path(File.dirname(File.dirname(__FILE__))), 'plugins')
  @plugin_manager.add_plugin_source plugin_dir
  
  @settings_manager.get('zmb', 'plugin_instances', []).each{|instance| load instance}
  @debug = @settings_manager.get('zmb', 'debug', false)
  
  trap("HUP") { @plugin_manager.refresh_plugin_sources; load "commands"; load "users" }
end

Instance Attribute Details

#debug(sender, message, exception = nil) ⇒ Object

Returns the value of attribute debug.



17
18
19
# File 'lib/zmb.rb', line 17

def debug
  @debug
end

#instancesObject

Returns the value of attribute instances.



17
18
19
# File 'lib/zmb.rb', line 17

def instances
  @instances
end

#plugin_managerObject

Returns the value of attribute plugin_manager.



17
18
19
# File 'lib/zmb.rb', line 17

def plugin_manager
  @plugin_manager
end

#plugin_sourcesObject

Returns the value of attribute plugin_sources.



17
18
19
# File 'lib/zmb.rb', line 17

def plugin_sources
  @plugin_sources
end

#settings_managerObject

Returns the value of attribute settings_manager.



17
18
19
# File 'lib/zmb.rb', line 17

def settings_manager
  @settings_manager
end

Instance Method Details

#addsource_command(e, source) ⇒ Object



393
394
395
396
397
# File 'lib/zmb.rb', line 393

def addsource_command(e, source)
  @plugin_sources << source
  @plugin_manager.add_plugin_source source
  "#{source} added to plugin manager"
end

#clone_command(e, instance, new_instance) ⇒ Object



379
380
381
382
383
384
385
386
# File 'lib/zmb.rb', line 379

def clone_command(e, instance, new_instance)
  if (settings = @settings_manager.setting(instance)) != {} then
    @settings_manager.save(new_instance, settings)
    "The settings for #{instance} were copied to #{new_instance}"
  else
    "No settings for #{instance}"
  end
end

#commandsObject



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/zmb.rb', line 277

def commands
  {
    'reload' => [:reload_command, 1, { :permission => 'admin' }],
    'unload' => [:unload_command, 1, { :permission => 'admin' }],
    'load' => [:load_command, 1, { :permission => 'admin' }],
    'save' => [:save_command, 0, { :permission => 'admin' }],
    'loaded' => [:loaded_command, 0, { :permission => 'admin' }],
    'setup' => [:setup_command, 2, { :permission => 'admin' }],
    'set' => [:set_command, 3, { :permission => 'admin' }],
    'get' => [:get_command, 2, { :permission => 'admin' }],
    'clone' => [:clone_command, 2, { :permission => 'admin' }],
    'reset' => [:reset_command, 1, { :permission => 'admin' }],
    'addsource' => [:addource_command, 1, { :permission => 'admin' }],
    'refresh' => [:refresh_command, 1, { :permission => 'admin' }],
    'quit' => [:quit_command, 0, { :permission => 'admin' }],
    'debug' => [:debug_command, 0, {
      :permission => 'admin',
      :help => 'Toggle debug' }],
    'stdout' => [:stdout_command, {
      :permission => 'admin',
      :usage => '/dev/null' }],
    'fork' => [:fork_command, 0, {
      :permission => 'admin' }],
  }
end

#debug_command(e) ⇒ Object



411
412
413
414
415
416
417
418
419
# File 'lib/zmb.rb', line 411

def debug_command(e)
  @debug = (not @debug)
  
  if @debug then
    "Debugging enabled"
  else
    "Debugging disabled"
  end
end

#event(sender, e) ⇒ Object



270
271
272
273
274
275
# File 'lib/zmb.rb', line 270

def event(sender, e)
  Thread.new do
    post! :pre_event, sender, e
    post! :event, sender, e
  end
end

#fork_command(e = nil) ⇒ Object



155
156
157
158
# File 'lib/zmb.rb', line 155

def fork_command(e=nil)
  run_fork
  "Forked"
end

#get_command(e, instance, key) ⇒ Object



371
372
373
374
375
376
377
# File 'lib/zmb.rb', line 371

def get_command(e, instance, key)
  if value = @settings_manager.get(instance, key) then
    "#{key} is #{value} for #{instance}"
  else
    "#{instance} or #{instance}/#{key} not found."
  end
end

#instanceObject



23
24
25
# File 'lib/zmb.rb', line 23

def instance
  'zmb'
end

#load(key) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/zmb.rb', line 99

def load(key)
  return true if @instances.has_key?(key)
  
  if p = @settings_manager.get(key, 'plugin') then
    object = @plugin_manager.plugin(p)
    return false if not object
    @instances[key] = object.new(self, @settings_manager.setting(key))
    @instances[key].class.send(:define_method, :plugin) { p }
    @instances[key].class.send(:define_method, :instance) { key }
    post! :plugin_loaded, key, @instances[key]
    true
  else
    false
  end
end

#load_command(e, instance) ⇒ Object



329
330
331
332
333
334
335
# File 'lib/zmb.rb', line 329

def load_command(e, instance)
  if not @instances.has_key?(instance) then
    load(instance) ? "#{instance} loaded" : "#{instance} did not load correctly"
  else
    "Instance already loaded #{instance}"
  end
end

#loaded_command(e) ⇒ Object



342
343
344
# File 'lib/zmb.rb', line 342

def loaded_command(e)
  @instances.keys.join(', ')
end

#pluginObject



19
20
21
# File 'lib/zmb.rb', line 19

def plugin
  'zmb'
end

#post(signal, *args) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/zmb.rb', line 227

def post(signal, *args)
  results = Array.new
  
  @instances.select{|name, instance| instance.respond_to?(signal)}.each do |name, instance|
    begin
      result = instance.send(signal, *args)
      break if result == :halt
      results << result
    rescue Exception
      debug(instance, "Sending signal `#{signal}` failed", $!)
    end
  end
  
  results
end

#post!(signal, *args) ⇒ Object

This will exclude the plugin manager



243
244
245
246
247
248
249
250
251
# File 'lib/zmb.rb', line 243

def post!(signal, *args) # This will exclude the plugin manager
  @instances.select{|name, instance| instance.respond_to?(signal) and instance != self}.each do |name, instance|
    begin
      break if instance.send(signal, *args) == :halt
    rescue Exception
      debug(instance, "Sending signal `#{signal}` failed", $!)
    end
  end
end

#quit_command(e) ⇒ Object



404
405
406
407
408
409
# File 'lib/zmb.rb', line 404

def quit_command(e)
  e.reply "Quitting"
  save
  @running = false
  instances.keys.each{ |i| unload(i) }
end

#refresh_command(e) ⇒ Object



399
400
401
402
# File 'lib/zmb.rb', line 399

def refresh_command(e)
  @plugin_manager.refresh_plugin_sources
  "Refreshed plugin sources"
end

#reload_command(e, instance) ⇒ Object



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/zmb.rb', line 303

def reload_command(e, instance)
  if @instances.has_key?(instance) then
    sockets = Array.new
    @sockets.each{ |sock,delegate| sockets << sock if delegate == @instances[instance] }
    unload(instance, false)
    reloaded = @plugin_manager.reload_plugin(@settings_manager.get(instance, 'plugin'))
    load(instance)
    
    sockets.each{ |socket| @sockets[socket] = @instances[instance] }
    @instances[instance].socket = sockets[0] if sockets.size == 1 and @instances[instance].respond_to?('socket=')
    
    reloaded ? "#{instance} reloaded" : "#{instance} refreshed"
  else
    "No such instance #{instance}"
  end
end

#reset_command(e, instance) ⇒ Object



388
389
390
391
# File 'lib/zmb.rb', line 388

def reset_command(e, instance)
  @settings_manager.save(instance, {})
  "Settings for #{instance} have been deleted."
end

#runObject



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/zmb.rb', line 125

def run
  debug(self, 'Start runloop')
  post! :running, self
  
  @running = true
  begin
    while @running
      socket_run(timeout)
      timer_run
    end
  rescue Interrupt
    debug(self, 'Runloop interrupted')
    save
  end
end

#run_forkObject



141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/zmb.rb', line 141

def run_fork
  @running = false
  pid = fork {
    STDIN.reopen(File.open('/dev/null','r'))
    STDOUT.reopen(File.open('/dev/null','w'))
    STDERR.reopen(File.open('/dev/null','w'))
    run
  }
  
  Process.detach(pid)
  debug(self, 'zmb Forked')
  pid
end

#running?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/zmb.rb', line 58

def running?
  @running
end

#saveObject



70
71
72
73
# File 'lib/zmb.rb', line 70

def save
  debug(self, "Saving settings")
  @instances.each{ |k,v| @settings_manager.save(k, v) }
end

#save_command(e) ⇒ Object



337
338
339
340
# File 'lib/zmb.rb', line 337

def save_command(e)
  save
  'settings saved'
end

#set_command(e, instance, key, value) ⇒ Object



359
360
361
362
363
364
365
366
367
368
369
# File 'lib/zmb.rb', line 359

def set_command(e, instance, key, value)
  settings = @settings_manager.setting(instance)
  settings[key] = value
  @settings_manager.save(instance, settings)
  
  if @instances.has_key?(instance) and @instances[instance].respond_to?('update') then
    @instances[instance].update(key, value)
  end
  
  "#{key} set to #{value} for #{instance}"
end

#settingsObject



62
63
64
65
66
67
68
# File 'lib/zmb.rb', line 62

def settings
  {
    'plugin_sources' => @plugin_sources,
    'plugin_instances' => @instances.keys,
    'debug' => @debug,
  }
end

#setup(plugin, instance) ⇒ Object



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/zmb.rb', line 253

def setup(plugin, instance)
  object = @plugin_manager.plugin plugin
  return false if not object
  
  s = Hash.new
  s['plugin'] = plugin
  
  if object.respond_to? 'wizard' then
    d = object.wizard
    d.each{ |k,v| s[k] = v['default'] if v.has_key?('default') and v['default'] }
  end
  
  @settings_manager.save instance, s
  
  true
end

#setup_command(e, plugin, instance = nil) ⇒ Object



346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/zmb.rb', line 346

def setup_command(e, plugin, instance=nil)
  instance = plugin if not instance
  
  if setup(plugin, instance) then
    object = @plugin_manager.plugin plugin
    result = ["Instance saved, please use the set command to override the default configuration for this instance."]
    result += object.wizard.map{ |k,v| "#{k} - #{v['help']} (default=#{v['default']})" } if object.respond_to? 'wizard'
    result.join("\n")
  else
    "plugin not found"
  end
end

#socket_add(delegate, socket) ⇒ Object



174
175
176
177
# File 'lib/zmb.rb', line 174

def socket_add(delegate, socket)
  debug(delegate, "Socked added")
  @sockets[socket] = delegate
end

#socket_delete(item) ⇒ Object



179
180
181
182
183
184
185
186
187
# File 'lib/zmb.rb', line 179

def socket_delete(item)
  if @sockets.has_value?(item) then
    @sockets.select{ |sock, delegate| delegate == item }.each{ |sock, delegate| @sockets.delete(sock) }
  end
  
  if @sockets.has_key?(item) then
    @sockets.delete(item)
  end
end

#socket_run(timeout) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/zmb.rb', line 189

def socket_run(timeout)
  result = select(@sockets.keys, nil, nil, timeout)
  
  if result != nil then
    result[0].select{|sock| @sockets.has_key?(sock)}.each do |sock|
      if sock.eof? then
        @sockets[sock].disconnected(self, sock) if @sockets[sock].respond_to?('disconnected')
        sock.close
        socket_delete sock
        debug(@sockets[sock], "Socket EOF")
      else
        @sockets[sock].received(self, sock, sock.gets()) if @sockets[sock].respond_to?('received')
      end
    end
  end
end

#stdout_command(e, out) ⇒ Object



421
422
423
424
# File 'lib/zmb.rb', line 421

def stdout_command(e, out)
  STDOUT.reopen(File.open(out,'w'))
  "STDOUT set to #{out}"
end

#timeoutObject



160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/zmb.rb', line 160

def timeout
  if timer_timeout > @maximum_timeout
    if @sockets.size < 1 then
      5
    else
      @maximum_timeout
    end
  elsif timer_timeout > @minimum_timeout
    timer_timeout
  else
    @minimum_timeout
  end
end

#timer_add(timer) ⇒ Object



206
207
208
209
# File 'lib/zmb.rb', line 206

def timer_add(timer)
  debug(timer.delegate, "Timer added (#{timer.symbol})")
  @timers << timer
end

#timer_delete(search) ⇒ Object



211
212
213
214
# File 'lib/zmb.rb', line 211

def timer_delete(search)
  @timers.each{ |timer| @timers.delete(timer) if timer.delegate == search }
  @timers.delete(search)
end

#timer_runObject



220
221
222
223
224
225
# File 'lib/zmb.rb', line 220

def timer_run
  @timers.select{|timer| timer.timeout <= 0.0 and timer.respond_to?("fire") }.each do |timer|
    debug(timer.delegate, "Timer #{timer.symbol} fired")
    timer.fire(self)
  end
end

#timer_timeoutObject

When will the next timer run?



216
217
218
# File 'lib/zmb.rb', line 216

def timer_timeout # When will the next timer run?
  @timers.map{|timer| timer.timeout}.sort.fetch(0, @maximum_timeout)
end

#unload(key, tell = true) ⇒ Object



115
116
117
118
119
120
121
122
123
# File 'lib/zmb.rb', line 115

def unload(key, tell=true)
  return false if not @instances.has_key?(key)
  instance = @instances.delete(key)
  @settings_manager.save key, instance
  socket_delete instance
  timer_delete instance
  instance.unloaded if instance.respond_to?('unloaded') and tell
  post! :plugin_unloaded, key, instance
end

#unload_command(e, instance) ⇒ Object



320
321
322
323
324
325
326
327
# File 'lib/zmb.rb', line 320

def unload_command(e, instance)
  if @instances.has_key?(instance) then
    unload(instance)
    "#{instance} unloaded"
  else
    "No such instance #{instance}"
  end
end