Class: Akaer::Application

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

Overview

The main Akaer application.

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Application) initialize(command)

Creates a new application.

Parameters:

  • command (Mamertes::Command)

    The current Mamertes command.



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
# File 'lib/akaer/application.rb', line 23

def initialize(command)
  @command = command
  application = @command.application

  # Setup logger
  Bovem::Logger.start_time = Time.now
  @logger = Bovem::Logger.create(Bovem::Logger.get_real_file(application.options["log-file"].value) || Bovem::Logger.default_file, Logger::INFO)

  # Open configuration
  begin
    overrides = {
      :interface => application.options["interface"].value,
      :addresses => application.options["addresses"].value,
      :start_address => application.options["start-address"].value,
      :aliases => application.options["aliases"].value,
      :add_command => application.options["add-command"].value,
      :remove_command => application.options["remove-command"].value,
      :log_file => application.options["log-file"].value,
      :log_level => application.options["log-level"].value,
      :dry_run => application.options["dry-run"].value,
      :quiet => application.options["quiet"].value
    }.reject {|k,v| v.nil? }

    @config = Akaer::Configuration.new(application.options["configuration"].value, overrides, @logger)

    @logger = nil
    @logger = self.get_logger
  rescue Bovem::Errors::InvalidConfiguration => e
    @logger ? @logger.fatal(e.message) : Bovem::Logger.create("STDERR").fatal("Cannot log to {mark=bright}#{config.log_file}{/mark}. Exiting...")
    raise ::SystemExit
  end

  self
end

Instance Attribute Details

- (Object) command (readonly)

The Mamertes command.



15
16
17
# File 'lib/akaer/application.rb', line 15

def command
  @command
end

- (Object) config (readonly)

The Configuration of this application.



12
13
14
# File 'lib/akaer/application.rb', line 12

def config
  @config
end

- (Object) logger

The logger for this application.



18
19
20
# File 'lib/akaer/application.rb', line 18

def logger
  @logger
end

Class Method Details

+ (Application) instance(command, force = false)

Returns a unique (singleton) instance of the application.

Parameters:

  • command (Mamertes::Command)

    The current Mamertes command.

  • force (Boolean) (defaults to: false)

    If to force recreation of the instance.

Returns:

  • (Application)

    The unique (singleton) instance of the application.



304
305
306
307
# File 'lib/akaer/application.rb', line 304

def self.instance(command, force = false)
  @instance = nil if force
  @instance ||= Akaer::Application.new(command)
end

Instance Method Details

- (Boolean) action_add

Adds aliases to the interface.

Returns:

  • (Boolean)

    true if action succedeed, false otherwise.



198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/akaer/application.rb', line 198

def action_add
  addresses = self.compute_addresses

  if addresses.present? then
    # Now, for every address, call the command
    addresses.all? {|address|
      self.manage(:add, address)
    }
  else
    @logger.error("No valid addresses to add to the interface found.") if !self.config.quiet
  end
end

- (Boolean) action_install

Installs the application into the autolaunch.

Returns:

  • (Boolean)

    true if action succedeed, false otherwise.



230
231
232
233
234
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
262
263
264
265
# File 'lib/akaer/application.rb', line 230

def action_install
  logger = get_logger

  if !self.is_osx? then
    logger.fatal("Install akaer on autolaunch is only available on MacOSX.") if !self.config.quiet
    return false
  end

  launch_agent = self.launch_agent_path

  begin
    logger.info("Creating the launch agent in {mark=bright}#{launch_agent}{/mark} ...") if !self.config.quiet

    args = $ARGV ? $ARGV[0, $ARGV.length - 1] : []

    plist = {"KeepAlive" => false, "Label" => "it.cowtech.akaer", "Program" => (::Pathname.new(Dir.pwd) + $0).to_s, "ProgramArguments" => args, "RunAtLoad" => true}
    ::File.open(launch_agent, "w") {|f|
      f.write(plist.to_json)
      f.flush
    }
    self.execute_command("plutil -convert binary1 \"#{launch_agent}\"")
  rescue => e
    logger.error("Cannot create the launch agent.") if !self.config.quiet
    return false
  end

  begin
    logger.info("Loading the launch agent ...") if !self.config.quiet
    self.execute_command("launchctl load -w \"#{launch_agent}\" > /dev/null 2>&1")
  rescue => e
    logger.error("Cannot load the launch agent.") if !self.config.quiet
    return false
  end

  true
end

- (Boolean) action_remove

Removes aliases from the interface.

Returns:

  • (Boolean)

    true if action succedeed, false otherwise.



214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/akaer/application.rb', line 214

def action_remove
  addresses = self.compute_addresses

  if addresses.present? then
    # Now, for every address, call the command
    addresses.all? {|address|
      self.manage(:remove, address)
    }
  else
    @logger.error("No valid addresses to remove from the interface found.") if !self.config.quiet
  end
end

- (Boolean) action_uninstall

Uninstalls the application from the autolaunch.

Returns:

  • (Boolean)

    true if action succedeed, false otherwise.



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/akaer/application.rb', line 270

def action_uninstall
  logger = self.get_logger

  if !self.is_osx? then
    logger.fatal("Install akaer on autolaunch is only available on MacOSX.") if !self.config.quiet
    return false
  end

  launch_agent = self.launch_agent_path

  # Unload the launch agent.
  begin
    self.execute_command("launchctl unload -w \"#{launch_agent}\" > /dev/null 2>&1")
  rescue => e
    logger.warn("Cannot unload the launch agent.") if !self.config.quiet
  end

  # Delete the launch agent.
  begin
    logger.info("Deleting the launch agent #{launch_agent} ...")
    ::File.delete(launch_agent)
  rescue => e
    logger.warn("Cannot delete the launch agent.") if !self.config.quiet
    return false
  end

  true
end

- (Array) compute_addresses(type = :all)

Computes the list of address to manage.

Parameters:

  • type (Symbol) (defaults to: :all)

    The type of addresses to consider. Valid values are :ipv4, :ipv6, otherwise all addresses are considered.

Returns:

  • (Array)

    The list of addresses to add or remove from the interface.



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
# File 'lib/akaer/application.rb', line 136

def compute_addresses(type = :all)
  rv = []

  if self.config.addresses.present? # We have an explicit list
    rv = self.config.addresses

    # Now filter the addresses
    filters = [type != :ipv6 ? :ipv4 : nil, type != :ipv4 ? :ipv6 : nil].compact
    rv = rv.select {|address|
      filters.any? {|filter| self.send("is_#{filter}?", address) }
    }.compact.uniq
  else
    begin
      ip = IPAddr.new(self.config.start_address.ensure_string)
      raise ArgumentError if (type == :ipv4 && !ip.ipv4?) || (type == :ipv6 && !ip.ipv6?)

      (self.config.aliases > 0 ? self.config.aliases : 5).times do
        rv << ip.to_s
        ip = ip.succ
      end
    rescue ArgumentError
    end
  end

  rv
end

- (Boolean) execute_command(command)

Executes a shell command.

Parameters:

  • command (String)

    The command to execute.

Returns:

  • (Boolean)

    true if command succeeded, false otherwise.



128
129
130
# File 'lib/akaer/application.rb', line 128

def execute_command(command)
  Kernel.system(command)
end

- (Logger) get_logger

Gets the current logger of the application.

Returns:

  • (Logger)

    The current logger of the application.



112
113
114
# File 'lib/akaer/application.rb', line 112

def get_logger
  @logger ||= Bovem::Logger.create(Bovem::Logger.default_file, @config.log_level, @log_formatter)
end

- (Boolean) is_ipv4?(address)

Checks if and address is a valid IPv4 address.

Parameters:

  • address (String)

    The address to check.

Returns:

  • (Boolean)

    true if the address is a valid IPv4 address, false otherwise.



71
72
73
74
75
76
# File 'lib/akaer/application.rb', line 71

def is_ipv4?(address)
  address = address.ensure_string

  mo = /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/.match(address)
  rv = (mo && mo.captures.all? {|i| i.to_i < 256}) ? true : false
end

- (Boolean) is_ipv6?(address)

Checks if and address is a valid IPv6 address.

Parameters:

  • address (String)

    The address to check.

Returns:

  • (Boolean)

    true if the address is a valid IPv6 address, false otherwise.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/akaer/application.rb', line 82

def is_ipv6?(address)
  address = address.ensure_string

  rv = catch(:valid) do
    # IPv6 (normal)
    throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ address
    throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ address
    throw(:valid, true) if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ address
    # IPv6 (IPv4 compat)
    throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ address && self.is_ipv4?($')
    throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ address && self.is_ipv4?($')
    throw(:valid, true) if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ address && self.is_ipv4?($')

    false
  end
end

- (Boolean) is_osx?

Checks if we are running on MacOS X.

System services are only available on that platform.

Returns:

  • (Boolean)

    true if the current platform is MacOS X, false otherwise.



63
64
65
# File 'lib/akaer/application.rb', line 63

def is_osx?
  ::Config::CONFIG['host_os'] =~ /^darwin/
end

- (String) launch_agent_path(name = "it.cowtech.akaer")

Gets the path for the launch agent file.

Parameters:

  • name (String) (defaults to: "it.cowtech.akaer")

    The base name for the agent.

Returns:

  • (String)

    The path for the launch agent file.



120
121
122
# File 'lib/akaer/application.rb', line 120

def launch_agent_path(name = "it.cowtech.akaer")
  ENV["HOME"] + "/Library/LaunchAgents/#{name}.plist"
end

- (Boolean) manage(type, address)

Adds or removes an alias from the interface.

Parameters:

  • type (Symbol)

    The operation to execute. Can be :add or :remove.

  • address (String)

    The address to manage.

Returns:

  • (Boolean)

    true if operation succedeed, false otherwise.



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
# File 'lib/akaer/application.rb', line 168

def manage(type, address)
  rv = true

  # Compute the command
  command = (type == :remove) ? self.config.remove_command : self.config.add_command

  # Interpolate
  command = command.gsub("@INTERFACE@", self.config.interface).gsub("@ALIAS@", address) + " > /dev/null 2>&1"

  # Compute the prefix
  @addresses ||= self.compute_addresses
  length = self.pad_number(@addresses.length)
  index = (@addresses.index(address) || 0) + 1
  prefix = "{mark=blue}[{mark=bright white}#{self.pad_number(index, length.length)}{mark=reset blue}/{/mark}#{length}{/mark}]{/mark}"

  # Now execute
  if !self.config.dry_run then
    @logger.info(@command.application.console.replace_markers("#{prefix} {mark=bright}#{(type == :remove ? "Removing" : "Adding")}{/mark} address {mark=bright}#{address}{/mark} #{type != :remove ? "to" : "from"} interface {mark=bright}#{self.config.interface}{/mark}...")) if !self.config.quiet
    rv = self.execute_command(command)
    @logger.error(@command.application.console.replace_markers("Cannot {mark=bright}#{(type == :remove ? "remove" : "add")}{/mark} address {mark=bright}#{address}{/mark} #{type != :remove ? "to" : "from"} interface {mark=bright}#{self.config.interface}{/mark}.")) if !rv
  else
    @logger.info(@command.application.console.replace_markers("#{prefix} I will {mark=bright}#{(type == :remove ? "remove" : "add")}{/mark} address {mark=bright}#{address}{/mark} #{type != :remove ? "to" : "from"} interface {mark=bright}#{self.config.interface}{/mark}.")) if !self.config.quiet
  end

  rv
end

- (String) pad_number(num, len = nil)

Pads a number to make it print friendly.

Parameters:

  • num (Fixnum)

    The number to pad.

  • len (Fixnum) (defaults to: nil)

    The minimum length of the padded string.

Returns:

  • (String)

    The padded number.



105
106
107
# File 'lib/akaer/application.rb', line 105

def pad_number(num, len = nil)
  num.to_integer.to_s.rjust([len.to_integer, 2].max, "0")
end