airport_events 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 47d41f6753ff18dd14d43e0fde4d204b792c5138
4
+ data.tar.gz: edd71ac189400ea7cd79bf1aa4318845d960bc74
5
+ SHA512:
6
+ metadata.gz: 0de2ac852c75c2b4240c27c45c5fe3510afc4f8ca8ea6298b410bf0bea3da5b2d3bd1fc1e2b74f669ba197a7fd0df27f95c7ffda497ca1ce9b34f44d25706179
7
+ data.tar.gz: e6e7f6e5831f3408b197fb43d7e20af41256ffb0aa76e764597d4f7a4ebd87b7094fe975640d57410fefd88343825272064c18bdab7d81f6f580b61587ace2fb
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ airport_events
2
+ ==============
3
+
4
+ airport-events is a MacOS specific library that receives WiFi related events. It currently supports:
5
+
6
+ ```
7
+ event parameters triggers when
8
+
9
+ connected String ssid, DateTime date A WiFi networks has been successfully connected.
10
+ disconnected DateTime date Computer is going to sleep, airport has been disabled or system halts.
11
+ ```
12
+
13
+ The library is extremely fresh and currently has no tests and has only been tested on MacOS 10.7.5.
14
+
15
+ I created it to log the amount of time I spend on different WiFi networks. Check out the airport-log utility below for an example implementation.
16
+
17
+ Why?
18
+ -------------
19
+
20
+ * Detect when a machine has joined a specific network.
21
+ * Log the amount of time that's spent on different networks.
22
+
23
+ Installation
24
+ ------------
25
+ ``
26
+ gem install airport-events
27
+ ``
28
+
29
+ The gem depends on an official Apple command line tool called "airport" which is disabled by default. To enable the "airport" command, symlink it into PATH by running the following command:
30
+
31
+ ``
32
+ sudo ln -s /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport /usr/sbin/airport
33
+ ``
34
+
35
+ Usage
36
+ -----
37
+
38
+ ```ruby
39
+ require 'airport_events'
40
+
41
+ # Create a new Watcher instance and set up some event listeners.
42
+ watcher = AirportEvents::Watcher.new
43
+
44
+ watcher.bind :connected do |ssid, date|
45
+ puts "Connected to #{ssid} on #{date}"
46
+ end
47
+
48
+ watcher.bind :disconnected do |date|
49
+ puts "Disconnected on #{date}"
50
+ end
51
+
52
+ # Start the watcher, which runs in a separate Thread.
53
+ watcher.start
54
+
55
+ # Keep program alive until interrupted (For instance: CTRL + C).
56
+ interrupted = false
57
+ trap("INT") { interrupted = true }
58
+
59
+ while true do
60
+ exit if interrupted
61
+ sleep 0.1
62
+ end
63
+
64
+ ```
65
+
66
+ Binary: airport-log
67
+ -----------
68
+
69
+ The gem is shipped with a binary called 'airport-log' which logs wifi connect/disconnect events to a JSON file.
70
+
71
+ For usage instructions run:
72
+ ``airport-log -h``
73
+
74
+ Example JSON output:
75
+
76
+ ```json
77
+ [
78
+ {
79
+ "ssid": "orca",
80
+ "connected_at": "2013-02-24T22:35:54+00:00",
81
+ "disconnected_at": "2013-02-24T22:47:38-05:00"
82
+ },
83
+ {
84
+ "ssid": "beluga",
85
+ "connected_at": "2013-02-24T22:48:23-05:00",
86
+ "disconnected_at": null
87
+ }
88
+ ]
89
+ ```
90
+
91
+ When disconnected_at is null it means the network is still connected or a disconnect event was never sent.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new('spec')
4
+
5
+ # If you want to make this the default task
6
+ task :default => :spec
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "airport_events/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'airport_events'
7
+ s.version = AirportEvents::VERSION
8
+ s.date = '2013-02-24'
9
+ s.summary = "A MacOS specific library to receive WiFi related events"
10
+ s.description = "Detect and log changes in WiFi network associations on MacOS. Utilizing the official, but hidden 'airport' command."
11
+ s.authors = ["Jonathan Pettersson"]
12
+ s.email = 'jonathan@spacetofu.com'
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.homepage = 'https://github.com/jpettersson/airport_events'
16
+ s.executables << 'airport-log'
17
+
18
+ s.add_development_dependency "rspec", "2.13.0"
19
+ s.add_runtime_dependency "thor", "0.17.0"
20
+ s.add_runtime_dependency "file-tail", "1.0.12"
21
+ s.add_runtime_dependency "json"
22
+ end
data/bin/airport-log ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/airport_events'
3
+ require "thor"
4
+
5
+ class AirportLogThor < Thor
6
+ desc 'enable_airport', 'Print instructions on how to enable the airport command in MacOS.'
7
+ def enable_airport
8
+ puts <<-EOF
9
+
10
+ The "airport" command is not enabled by default in MacOS. To enable it, run the following command:
11
+ sudo ln -s /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport /usr/sbin/airport
12
+
13
+ EOF
14
+ end
15
+
16
+ desc 'to_file --path PATH', "(Default) Log WiFi events to a JSON file. Default PATH is: ~/.airport-log.json"
17
+ method_option :path, :default => "#{Dir.home}/.airport-log.json", :aliases => '-p'
18
+ def to_file
19
+ puts "AirportLog: Logging WiFi events to '#{options[:path]}'"
20
+ AirportEvents::Logger.new options[:path]
21
+ end
22
+
23
+ default_task :to_file
24
+ end
25
+
26
+ AirportLogThor.start
@@ -0,0 +1,41 @@
1
+ require_relative 'airport_events/event_dispatcher'
2
+ require_relative 'airport_events/airport'
3
+ require_relative 'airport_events/kernel_log_parser'
4
+ require_relative 'airport_events/logger'
5
+
6
+ module AirportEvents
7
+ class Watcher
8
+ include EventDispatcher
9
+
10
+ def start
11
+ Airport.ensure_airport_command
12
+
13
+ if Airport.connected?
14
+ connected DateTime.now
15
+ else
16
+ disconnected DateTime.now
17
+ end
18
+
19
+ @kernel_log_parser = KernelLogParser.new
20
+
21
+ @kernel_log_parser.bind :connected do |date|
22
+ connected date
23
+ end
24
+
25
+ @kernel_log_parser.bind :disconnected do |date|
26
+ disconnected date
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def connected date
33
+ trigger :connected, Airport.ssid, date
34
+ end
35
+
36
+ def disconnected date
37
+ trigger :disconnected, date
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ module AirportEvents
2
+ class Airport
3
+ INFO_COMMAND = "airport -I 2>&1"
4
+
5
+ def self.ensure_airport_command
6
+ run_info_command
7
+ unless $?.exitstatus == 0
8
+ raise "Fatal: the 'airport' command is not in PATH. Run 'airport-log enable_airport' for instructions."
9
+ end
10
+ end
11
+
12
+ def self.ssid
13
+ parse_ssid(run_info_command)
14
+ end
15
+
16
+ def self.connected?
17
+ ssid.length > 0
18
+ end
19
+
20
+ def self.run_info_command
21
+ %x(#{INFO_COMMAND})
22
+ end
23
+
24
+ def self.parse_ssid result
25
+ if result.match /\sSSID: /
26
+ result[/\sSSID: (.*?)\n/, 1]
27
+ else
28
+ ""
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ module AirportEvents
2
+ module EventDispatcher
3
+
4
+ module InstanceMethods
5
+ def bind(event, &blk)
6
+ if observers[event].nil?
7
+ observers[event] = Array.new
8
+ end
9
+
10
+ observers[event].push blk
11
+ end
12
+
13
+ # TODO: unbind
14
+ #def unbind
15
+ #
16
+ #end
17
+
18
+ def trigger(event, *args)
19
+ unless observers[event].nil?
20
+ observers[event].each do |blk|
21
+ blk.call *args
22
+ end
23
+ end
24
+ end
25
+
26
+ def observers
27
+ @observers ||= Hash.new
28
+ end
29
+ end
30
+
31
+ def self.included(base)
32
+ base.send :include, InstanceMethods
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ require 'file-tail'
2
+ require 'date'
3
+
4
+ module AirportEvents
5
+ class KernelLogParser
6
+ include EventDispatcher
7
+
8
+ def initialize
9
+ Thread.new do
10
+ File.open('/var/log/kernel.log') do |log|
11
+ log.extend(File::Tail)
12
+ log.interval = 10
13
+ log.backward(0)
14
+ log.tail do |line|
15
+
16
+ if event = match_event(line)
17
+ trigger event, DateTime.strptime(line[0..15], '%b %d %H:%M:%S')
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def match_event line
25
+ if line.match /AirPort: Link Up.*/
26
+ :connected
27
+ elsif line.match /AirPort: Link Down.*/
28
+ :disconnected
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,100 @@
1
+ require 'json'
2
+ require 'logger'
3
+
4
+ OUT = Logger.new(STDOUT)
5
+
6
+ module AirportEvents
7
+ class Logger
8
+
9
+ def initialize path
10
+ @log_path = path
11
+ read_log
12
+
13
+ watcher = AirportEvents::Watcher.new
14
+
15
+ watcher.bind :connected do |ssid, date|
16
+ connected ssid, date
17
+ end
18
+
19
+ watcher.bind :disconnected do |date|
20
+ disconnected date
21
+ end
22
+
23
+ watcher.start
24
+
25
+ interrupted = false
26
+ trap("INT") { interrupted = true }
27
+
28
+ while true do
29
+ exit if interrupted
30
+ sleep 0.1
31
+ end
32
+
33
+ end
34
+
35
+ def connected ssid, date
36
+ if last_entry
37
+ unless last_entry[:ssid] == ssid
38
+ new_entry ssid, date
39
+ else
40
+ OUT.info "AirportLogger: Already connected to to \"#{ssid}\""
41
+ end
42
+ else
43
+ new_entry ssid, date
44
+ end
45
+ end
46
+
47
+ def disconnected date
48
+ end_last_entry date
49
+ end
50
+
51
+ def read_log
52
+ @log = read_file
53
+ end
54
+
55
+ def read_file
56
+ if file = File.read(@log_path, mode: 'a+')
57
+ if file.length > 0
58
+ JSON.parse(file, :symbolize_names => true)
59
+ else
60
+ []
61
+ end
62
+ end
63
+ end
64
+
65
+ def rewrite
66
+ IO.write(@log_path, JSON.pretty_generate(@log))
67
+ read_log
68
+ end
69
+
70
+ def append entry
71
+ @log.push entry
72
+ rewrite
73
+ end
74
+
75
+ def last_entry
76
+ entry = @log.last
77
+ if entry and entry[:disconnected_at].nil?
78
+ return entry
79
+ end
80
+ end
81
+
82
+ def new_entry ssid, date
83
+ OUT.info "AirportLogger: Connected to \"#{ssid}\""
84
+ append({
85
+ :ssid => ssid,
86
+ :connected_at => date,
87
+ :disconnected_at => nil
88
+ })
89
+ end
90
+
91
+ def end_last_entry date
92
+ if last_entry and last_entry[:disconnected_at].nil?
93
+ OUT.info "AirportLogger: Disconnected"
94
+ last_entry[:disconnected_at] = date
95
+ rewrite
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,3 @@
1
+ module AirportEvents
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,15 @@
1
+ agrCtlRSSI: -48
2
+ agrExtRSSI: 0
3
+ agrCtlNoise: -89
4
+ agrExtNoise: 0
5
+ state: running
6
+ op mode: station
7
+ lastTxRate: 216
8
+ maxRate: 300
9
+ lastAssocStatus: 0
10
+ 802.11 auth: open
11
+ link auth: wpa2-psk
12
+ BSSID: 2:18:1a:31:50:21
13
+ SSID: 3rd Ward Morgan
14
+ MCS: 13
15
+ channel: 44,1
@@ -0,0 +1,9 @@
1
+ Feb 23 13:44:05 minnow kernel[0]: AirPort: Link Down on en0. Reason 8 (Disassociated because station leaving).
2
+ Feb 23 13:44:32 minnow kernel[0]: AirPort: Link Up on en0
3
+ Feb 23 13:44:32 minnow kernel[0]: AirPort: Link Up on en0
4
+ Feb 23 13:44:32 minnow kernel[0]: AirPort: Link Up on en0
5
+ Feb 23 13:44:05 minnow kernel[0]: AirPort: Link Down on en0. Reason 8 (Disassociated because station leaving).
6
+ Feb 23 13:44:05 minnow kernel[0]: AirPort: Link Down on en0. Reason 8 (Disassociated because station leaving).
7
+ Feb 23 13:44:32 minnow kernel[0]: AirPort: Link Up on en0
8
+ Feb 23 13:44:05 minnow kernel[0]: AirPort: Link Down on en0. Reason 8 (Disassociated because station leaving).
9
+ Feb 23 13:44:32 minnow kernel[0]: AirPort: Link Up on en0
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require_relative '../lib/airport_events'
5
+
6
+ RSpec.configure do |config|
7
+ # some (optional) config here
8
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: airport_events
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Pettersson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-02-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 2.13.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 2.13.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.17.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.17.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: file-tail
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.12
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.12
55
+ - !ruby/object:Gem::Dependency
56
+ name: json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Detect and log changes in WiFi network associations on MacOS. Utilizing
70
+ the official, but hidden 'airport' command.
71
+ email: jonathan@spacetofu.com
72
+ executables:
73
+ - airport-log
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - .rspec
79
+ - Gemfile
80
+ - README.md
81
+ - Rakefile
82
+ - airport_events.gemspec
83
+ - bin/airport-log
84
+ - lib/airport_events.rb
85
+ - lib/airport_events/airport.rb
86
+ - lib/airport_events/event_dispatcher.rb
87
+ - lib/airport_events/kernel_log_parser.rb
88
+ - lib/airport_events/logger.rb
89
+ - lib/airport_events/version.rb
90
+ - spec/fixtures/airport.sample.txt
91
+ - spec/fixtures/kernel.log.sample
92
+ - spec/spec_helper.rb
93
+ homepage: https://github.com/jpettersson/airport_events
94
+ licenses: []
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 2.0.0
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: A MacOS specific library to receive WiFi related events
116
+ test_files:
117
+ - spec/fixtures/airport.sample.txt
118
+ - spec/fixtures/kernel.log.sample
119
+ - spec/spec_helper.rb