airport_events 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/Gemfile +2 -0
- data/README.md +91 -0
- data/Rakefile +6 -0
- data/airport_events.gemspec +22 -0
- data/bin/airport-log +26 -0
- data/lib/airport_events.rb +41 -0
- data/lib/airport_events/airport.rb +32 -0
- data/lib/airport_events/event_dispatcher.rb +36 -0
- data/lib/airport_events/kernel_log_parser.rb +32 -0
- data/lib/airport_events/logger.rb +100 -0
- data/lib/airport_events/version.rb +3 -0
- data/spec/fixtures/airport.sample.txt +15 -0
- data/spec/fixtures/kernel.log.sample +9 -0
- data/spec/spec_helper.rb +8 -0
- metadata +119 -0
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
data/.rspec
ADDED
data/Gemfile
ADDED
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,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,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
|
data/spec/spec_helper.rb
ADDED
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
|