panopticon 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/panopticond +52 -0
- data/extra/install.sh +14 -0
- data/extra/panopticond +62 -0
- data/extra/public/css/bootstrap-theme.css +384 -0
- data/extra/public/css/bootstrap-theme.min.css +1 -0
- data/extra/public/css/bootstrap.css +6805 -0
- data/extra/public/css/bootstrap.min.css +9 -0
- data/extra/public/css/panopticon.css +15 -0
- data/extra/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/extra/public/fonts/glyphicons-halflings-regular.svg +228 -0
- data/extra/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/extra/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/extra/public/index.html +135 -0
- data/extra/public/js/bootstrap.js +1999 -0
- data/extra/public/js/bootstrap.min.js +6 -0
- data/extra/public/js/canvasjs.min.js +555 -0
- data/extra/public/js/jquery-3.1.0.min.js +4 -0
- data/extra/public/js/script.js +512 -0
- data/lib/panopticon.rb +10 -0
- data/lib/panopticon/api.rb +59 -0
- data/lib/panopticon/command/panopticond.rb +47 -0
- data/lib/panopticon/log.rb +36 -0
- data/lib/panopticon/version.rb +3 -0
- data/lib/panopticon/wlan_capture.rb +135 -0
- data/lib/panopticon/wlan_control.rb +275 -0
- data/lib/panopticon/wlan_utilization.rb +128 -0
- data/panopticon.gemspec +30 -0
- metadata +151 -0
data/lib/panopticon.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require "panopticon/version"
|
2
|
+
require "panopticon/api"
|
3
|
+
require "panopticon/log"
|
4
|
+
require "panopticon/wlan_capture"
|
5
|
+
require "panopticon/wlan_control"
|
6
|
+
require "panopticon/wlan_utilization"
|
7
|
+
require "panopticon/command/panopticond"
|
8
|
+
|
9
|
+
module Panopticon
|
10
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Panopticon
|
2
|
+
require "sinatra/base"
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
require "panopticon/wlan_control"
|
6
|
+
|
7
|
+
class APIServer < Sinatra::Base
|
8
|
+
DEFAULT_PORT=8080
|
9
|
+
|
10
|
+
set :port, DEFAULT_PORT
|
11
|
+
set :public_folder, File.dirname(__FILE__) + "/../../extra/public"
|
12
|
+
enable :logging
|
13
|
+
|
14
|
+
def self.run! arg={}
|
15
|
+
@arg = arg
|
16
|
+
|
17
|
+
@port = arg[:port] || DEFAULT_PORT
|
18
|
+
set :port, @port
|
19
|
+
|
20
|
+
@@wlan_control = Panopticon::WlanControl.new(arg)
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
get "/" do
|
25
|
+
redirect to ("/index.html")
|
26
|
+
end
|
27
|
+
|
28
|
+
get "/api/v1/status" do
|
29
|
+
JSON.dump(@@wlan_control.get_status)
|
30
|
+
end
|
31
|
+
|
32
|
+
post "/api/v1/start" do
|
33
|
+
begin
|
34
|
+
data = request.body.read
|
35
|
+
json = JSON.parse(data)
|
36
|
+
|
37
|
+
@@wlan_control.start_capture(json)
|
38
|
+
status 200
|
39
|
+
"success"
|
40
|
+
rescue => e
|
41
|
+
$log.err("/start failed => #{e}")
|
42
|
+
status 500
|
43
|
+
"failed (#{e})"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
post "/api/v1/stop" do
|
48
|
+
begin
|
49
|
+
@@wlan_control.stop_capture
|
50
|
+
status 200
|
51
|
+
"success"
|
52
|
+
rescue => e
|
53
|
+
$log.err("/start failed => #{e}")
|
54
|
+
status 500
|
55
|
+
"failed (#{e})"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Panopticon
|
2
|
+
require "panopticon/log"
|
3
|
+
|
4
|
+
class Daemon
|
5
|
+
DEFAULT_CONFIG_PATH="/etc/panopticon.conf"
|
6
|
+
|
7
|
+
DEFAULT_API_PORT=8080
|
8
|
+
|
9
|
+
DEFAULT_IFNAME="wlan0"
|
10
|
+
DEFAULT_CAPTURE_PATH="/cap"
|
11
|
+
DEFAULT_LOG_FILE="/var/log/panopticond.log"
|
12
|
+
|
13
|
+
def self.default_options
|
14
|
+
{
|
15
|
+
# config file (exclusive)
|
16
|
+
:config_file => DEFAULT_CONFIG_PATH,
|
17
|
+
|
18
|
+
# daemon parameters
|
19
|
+
:port => DEFAULT_API_PORT,
|
20
|
+
|
21
|
+
# capture parameters
|
22
|
+
:ifname => DEFAULT_IFNAME,
|
23
|
+
:capture_path => DEFAULT_CAPTURE_PATH,
|
24
|
+
|
25
|
+
# log
|
26
|
+
:log_file => DEFAULT_LOG_FILE,
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize arg={}
|
31
|
+
@arg = arg
|
32
|
+
|
33
|
+
@config_file = arg[:config_file]
|
34
|
+
|
35
|
+
@arg = read_config(@config_file)
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
Panopticon::APIServer.run!(@arg)
|
40
|
+
end
|
41
|
+
|
42
|
+
def read_config path
|
43
|
+
# notimp
|
44
|
+
@arg
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Log
|
2
|
+
require "logger"
|
3
|
+
def initialize opts={}
|
4
|
+
@debug_mode = opts[:debug_mode] || false
|
5
|
+
@output = opts[:output] || STDOUT
|
6
|
+
|
7
|
+
case @output
|
8
|
+
when "STDOUT"
|
9
|
+
@output = STDOUT
|
10
|
+
when "STDERR"
|
11
|
+
@output = STDERR
|
12
|
+
end
|
13
|
+
@logger = Logger.new(@output)
|
14
|
+
|
15
|
+
@logger.datetime_format = "%Y%m%d%H%m%S"
|
16
|
+
@logger.formatter = proc { |severity, datetime, progname, msg|
|
17
|
+
"[#{datetime}] #{progname}\t#{severity}: #{msg}\n"
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def warn str
|
22
|
+
@logger.warn(str)
|
23
|
+
end
|
24
|
+
|
25
|
+
def err str
|
26
|
+
@logger.error(str)
|
27
|
+
end
|
28
|
+
|
29
|
+
def info str
|
30
|
+
@logger.info(str)
|
31
|
+
end
|
32
|
+
|
33
|
+
def debug str
|
34
|
+
@logger.debug(str)
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Panopticon
|
2
|
+
require "open3"
|
3
|
+
require "panopticon/wlan_utilization"
|
4
|
+
|
5
|
+
class WlanCapture
|
6
|
+
attr_reader :duration, :current_channel, :filesize, :channel_walk, :utilization, :utilization_channel
|
7
|
+
CHAN = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
8
|
+
34, 36, 38, 40, 42, 44, 46, 48,
|
9
|
+
52, 56, 60, 64,
|
10
|
+
100, 104, 108, 112, 116,
|
11
|
+
120, 124, 128, 132, 136, 140,
|
12
|
+
149, 153, 157, 161, 165]
|
13
|
+
LIMIT_IDX=CHAN.length
|
14
|
+
|
15
|
+
DEFAULT_IFNAME="wlan0"
|
16
|
+
DEFAULT_INTERVAL=1
|
17
|
+
|
18
|
+
def initialize ifname=DEFAULT_IFNAME, channels=CHAN, interval = DEFAULT_INTERVAL
|
19
|
+
@th_shark = nil
|
20
|
+
@th_capture = nil
|
21
|
+
@black_list = []
|
22
|
+
|
23
|
+
@ifname = ifname || DEFAULT_IFNAME
|
24
|
+
@channels = channels || CHAN
|
25
|
+
@interval = interval || DEFAULT_INTERVAL
|
26
|
+
|
27
|
+
init_status()
|
28
|
+
|
29
|
+
@wlan_utilization = Panopticon::WlanUtilization.new(@ifname)
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_capture fpath
|
33
|
+
init_device()
|
34
|
+
init_status()
|
35
|
+
start_time = Time.now.to_i
|
36
|
+
|
37
|
+
stdin, stdout, stderr, @th_tshark = *Open3.popen3(
|
38
|
+
"tshark -i #{@ifname} -F pcapng -w #{fpath}")
|
39
|
+
|
40
|
+
while @th_tshark.alive?
|
41
|
+
sleep 1
|
42
|
+
|
43
|
+
# update status
|
44
|
+
@duration = Time.now.to_i - start_time
|
45
|
+
@filesize = File.size?(fpath) || 0
|
46
|
+
|
47
|
+
# do something here to run before channel transition
|
48
|
+
ary = @wlan_utilization.current_data()
|
49
|
+
@utilization_channel = ary[0]
|
50
|
+
@utilization = ary[3]
|
51
|
+
|
52
|
+
prev_channel = @current_channel
|
53
|
+
@current_chanenl = move_channel(@current_channel, @channels)
|
54
|
+
@channel_walk += 1
|
55
|
+
$log.debug("channel moved to #{@current_channel} from #{prev_channel} (dur=#{@duration}, size=#{@filesize}, walk=#{@channel_walk}, utilization=#{@utilization} uch=#{@utilization_channel})")
|
56
|
+
end
|
57
|
+
rescue => e
|
58
|
+
$log.warn("run_capture detected unknown error (#{e})")
|
59
|
+
end
|
60
|
+
|
61
|
+
def stop_capture
|
62
|
+
if @th_tshark == nil
|
63
|
+
$log.err("tried to kill tshark, but it's not executed? (or already dead?)")
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
Process.kill("INT", @th_tshark.pid)
|
68
|
+
end
|
69
|
+
|
70
|
+
def modify_channels channels
|
71
|
+
@channels = channels
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def init_status
|
76
|
+
@duration = 0
|
77
|
+
@current_channel = @channels[0] || 1
|
78
|
+
@filesize = 0
|
79
|
+
@channel_walk = 0
|
80
|
+
end
|
81
|
+
|
82
|
+
def init_device
|
83
|
+
unless @ifname.match(/^wlan\d+$/)
|
84
|
+
$log.debug("non-wlan device skips device initialization")
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
unless system("ip link set #{@ifname} down")
|
89
|
+
raise "failed to turn down #{@ifname}"
|
90
|
+
end
|
91
|
+
unless system("iw #{@ifname} set monitor fcsfail otherbss control")
|
92
|
+
raise "failed to set #{@ifname} to monitor mode"
|
93
|
+
end
|
94
|
+
unless system("ip link set #{@ifname} up")
|
95
|
+
raise "failed to turn up #{@ifname}"
|
96
|
+
end
|
97
|
+
unless system("iw wlan0 set channel #{@current_channel}")
|
98
|
+
raise "failed to set channel #{@current_channel} on #{@ifname}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def move_channel current, channels
|
103
|
+
unless @ifname.match(/^wlan\d+$/)
|
104
|
+
$log.debug("non-wlan device skips channel transition")
|
105
|
+
return
|
106
|
+
end
|
107
|
+
|
108
|
+
next_channel = pick_channel(current, channels)
|
109
|
+
|
110
|
+
while !system("iw #{@ifname} set channel #{next_channel}")
|
111
|
+
# what if we got unplugged ifname? => should we die?
|
112
|
+
$log.debug("channel transition failed, added to black list (channel=#{next_channel})")
|
113
|
+
@black_list << next_channel
|
114
|
+
sleep 1
|
115
|
+
next_channel = pick_channel(next_channel, channels)
|
116
|
+
end
|
117
|
+
|
118
|
+
@current_channel = next_channel
|
119
|
+
end
|
120
|
+
|
121
|
+
def pick_channel current, channels
|
122
|
+
idx = channels.index(current)
|
123
|
+
idx += 1
|
124
|
+
next_channel = channels[idx % channels.length] || 1
|
125
|
+
|
126
|
+
# we have black list
|
127
|
+
while @black_list.include?(next_channel)
|
128
|
+
idx += 1
|
129
|
+
next_channel = channels[idx % channels.length] || 1
|
130
|
+
end
|
131
|
+
|
132
|
+
return next_channel
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,275 @@
|
|
1
|
+
module Panopticon
|
2
|
+
require "json"
|
3
|
+
require "thread"
|
4
|
+
|
5
|
+
require "panopticon/log"
|
6
|
+
require "panopticon/wlan_capture"
|
7
|
+
|
8
|
+
class WlanControl
|
9
|
+
DEFAULT_IFNAME = "wlan0"
|
10
|
+
DEFAULT_CAPTURE_PATH = "/cap"
|
11
|
+
|
12
|
+
STATE_INIT = :INIT
|
13
|
+
STATE_RUNNING = :RUNNING
|
14
|
+
STATE_STOP = :STOP
|
15
|
+
|
16
|
+
def self.default_options
|
17
|
+
return {
|
18
|
+
:ifname => DEFAULT_IFNAME,
|
19
|
+
:capture_path => DEFAULT_CAPTURE_PATH,
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize args={}
|
24
|
+
@th_capture = nil
|
25
|
+
@wlan_capture = nil
|
26
|
+
@state = STATE_INIT
|
27
|
+
|
28
|
+
@ifname = args[:ifname] || DEFAULT_IFNAME
|
29
|
+
$log.debug("hogeeee #{@ifname} #{args[:ifname]}")
|
30
|
+
@capture_path = args[:capture_path] || DEFAULT_CAPTURE_PATH
|
31
|
+
|
32
|
+
@hostname = `hostname`
|
33
|
+
end
|
34
|
+
|
35
|
+
def disk_info
|
36
|
+
line = `df`.split("\n")[1].split # assumes first line to be "/"
|
37
|
+
size = line[1].to_i * 512
|
38
|
+
used = line[2].to_i * 512
|
39
|
+
|
40
|
+
return {:size => size, :used => used }
|
41
|
+
end
|
42
|
+
|
43
|
+
def memory_info
|
44
|
+
if File.exists?("/proc/meminfo")
|
45
|
+
# linux or bsd
|
46
|
+
return memory_info_from_proc
|
47
|
+
else
|
48
|
+
# mac os x ?
|
49
|
+
return memory_info_from_mac
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def memory_info_from_proc
|
54
|
+
total = 0
|
55
|
+
free = 0
|
56
|
+
used = 0
|
57
|
+
File.open("/proc/meminfo") do |file|
|
58
|
+
total = file.gets.split[1].to_i
|
59
|
+
free = file.gets.split[1].to_i
|
60
|
+
used = total - free
|
61
|
+
end
|
62
|
+
|
63
|
+
return {:size => total, :used => used}
|
64
|
+
end
|
65
|
+
|
66
|
+
def memory_info_from_mac
|
67
|
+
_, total = `sysctl hw.memsize`.split.map{|n| n.to_i}
|
68
|
+
|
69
|
+
used = 0
|
70
|
+
app_mem = 0
|
71
|
+
comp_mem = 0
|
72
|
+
file_backed_mem = 0
|
73
|
+
`vm_stat`.split("\n").each do |line|
|
74
|
+
match = line.match(/^Anonymous pages: *(\d+)\.$/)
|
75
|
+
if match
|
76
|
+
app_mem = match[1].to_i * 4096
|
77
|
+
end
|
78
|
+
|
79
|
+
match = line.match(/^Pages occupied by compressor: *(\d+)\.$/)
|
80
|
+
if match
|
81
|
+
comp_mem = match[1].to_i * 4096
|
82
|
+
end
|
83
|
+
|
84
|
+
match = line.match(/^File-backed pages: *(\d+)\.$/)
|
85
|
+
if match
|
86
|
+
file_backed_mem = match[1].to_i * 4096
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
used = (app_mem + comp_mem + file_backed_mem)
|
91
|
+
|
92
|
+
return {:size => total, :used => used}
|
93
|
+
end
|
94
|
+
|
95
|
+
def cpu_info
|
96
|
+
if File.exists?("/proc/stat")
|
97
|
+
return cpu_info_from_proc
|
98
|
+
else
|
99
|
+
return cpu_info_from_mac
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def cpu_info_from_proc
|
104
|
+
used = 0
|
105
|
+
|
106
|
+
current_cpu_info = []
|
107
|
+
File.open("/proc/stat") do |file|
|
108
|
+
current_cpu_info = file.gets.split[1..4].map{|elm| elm.to_i}
|
109
|
+
end
|
110
|
+
|
111
|
+
if @prev_cpu_info == nil
|
112
|
+
@prev_cpu_info = current_cpu_info
|
113
|
+
return 0
|
114
|
+
end
|
115
|
+
|
116
|
+
usage_sub = current_cpu_info[0..2].inject(0){|sum, elm| sum += elm} -
|
117
|
+
@prev_cpu_info[0..2].inject(0){|sum, elm| sum += elm}
|
118
|
+
total_sub = current_cpu_info.inject(0){|sum, elm| sum += elm} -
|
119
|
+
@prev_cpu_info.inject(0){|sum, elm| sum += elm}
|
120
|
+
|
121
|
+
@prev_cpu_info = current_cpu_info
|
122
|
+
|
123
|
+
used = ((usage_sub.to_f * 100) / total_sub)
|
124
|
+
|
125
|
+
return used
|
126
|
+
end
|
127
|
+
|
128
|
+
def cpu_info_from_mac
|
129
|
+
line = `top | head -4 | grep CPU`
|
130
|
+
match = line.match(/^CPU usage: *(\d+\.\d+)% user, *(\d+\.\d+)% sys, *(\d+\.\d+)% idle $/)
|
131
|
+
return 0.0 unless match
|
132
|
+
|
133
|
+
user = match[1].to_f
|
134
|
+
sys = match[2].to_f
|
135
|
+
idle = match[3].to_f
|
136
|
+
used = 100 - idle
|
137
|
+
|
138
|
+
return used
|
139
|
+
end
|
140
|
+
|
141
|
+
def capture_file_info
|
142
|
+
filesize = 0
|
143
|
+
duration = 0
|
144
|
+
|
145
|
+
if @wlan_capture
|
146
|
+
filesize = @wlan_capture.filesize
|
147
|
+
duration = @wlan_capture.duration
|
148
|
+
end
|
149
|
+
|
150
|
+
return {
|
151
|
+
:filename => @filename,
|
152
|
+
:filesize => filesize,
|
153
|
+
:duration => duration,
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
def wlan_info
|
158
|
+
info = {
|
159
|
+
:current_channel => 0,
|
160
|
+
:channel_walk => 0,
|
161
|
+
:utilization => 0,
|
162
|
+
:utilization_channel => 0,
|
163
|
+
}
|
164
|
+
|
165
|
+
unless @state == STATE_RUNNING
|
166
|
+
return info
|
167
|
+
end
|
168
|
+
|
169
|
+
if @wlan_capture.nil?
|
170
|
+
$log.err("state mismatch (state = #{@state} <=> capture is running");
|
171
|
+
return
|
172
|
+
end
|
173
|
+
|
174
|
+
info[:current_channel] = @wlan_capture.current_channel
|
175
|
+
info[:channel_walk] = @wlan_capture.channel_walk
|
176
|
+
info[:utilization] = @wlan_capture.utilization
|
177
|
+
info[:utilization_channel] = @wlan_capture.utilization_channel
|
178
|
+
|
179
|
+
return info
|
180
|
+
end
|
181
|
+
|
182
|
+
def generate_filename
|
183
|
+
return "#{Time.now.strftime("%Y%m%d%H%m%S")}_#{@ifname}_#{$$}.pcapng"
|
184
|
+
end
|
185
|
+
|
186
|
+
# request handler for API
|
187
|
+
def start_capture arg
|
188
|
+
$log.info("starting new capture")
|
189
|
+
|
190
|
+
if @state == STATE_RUNNING
|
191
|
+
$log.warn("WlanCapture instance is already running")
|
192
|
+
return
|
193
|
+
end
|
194
|
+
|
195
|
+
if @th_capture
|
196
|
+
$log.warn("capture thread is already running, terminate it first")
|
197
|
+
return
|
198
|
+
end
|
199
|
+
|
200
|
+
if @wlan_capture
|
201
|
+
$log.warn("capture instance is running, teminate it first")
|
202
|
+
return
|
203
|
+
end
|
204
|
+
|
205
|
+
$log.info("starting new capture instance")
|
206
|
+
@wlan_capture = Panopticon::WlanCapture.new(@ifname)
|
207
|
+
|
208
|
+
# start capturing
|
209
|
+
filename = generate_filename()
|
210
|
+
@th_capture = Thread.new do
|
211
|
+
@wlan_capture.run_capture("#{@capture_path}/#{filename}")
|
212
|
+
end
|
213
|
+
$log.info("started new capture at #{filename}")
|
214
|
+
|
215
|
+
# move state
|
216
|
+
@state = STATE_RUNNING
|
217
|
+
@filename = filename
|
218
|
+
end
|
219
|
+
|
220
|
+
def stop_capture
|
221
|
+
if @state == STATE_INIT or @state == STATE_STOP
|
222
|
+
$log.err("stopping not runnning capture instance")
|
223
|
+
return
|
224
|
+
end
|
225
|
+
|
226
|
+
if @wlan_capture
|
227
|
+
@wlan_capture.stop_capture
|
228
|
+
end
|
229
|
+
|
230
|
+
if @th_capture and @th_capture.alive?
|
231
|
+
$log.info("stopping capture thread")
|
232
|
+
@th_capture.join
|
233
|
+
end
|
234
|
+
|
235
|
+
@wlan_capture = nil
|
236
|
+
@th_capture = nil
|
237
|
+
|
238
|
+
@state = STATE_STOP
|
239
|
+
end
|
240
|
+
|
241
|
+
def change_channels arg
|
242
|
+
end
|
243
|
+
|
244
|
+
def get_config
|
245
|
+
end
|
246
|
+
|
247
|
+
def get_status
|
248
|
+
@cached_disk_info = disk_info()
|
249
|
+
@cached_memory_info = memory_info()
|
250
|
+
@cached_cpu_info = cpu_info()
|
251
|
+
@cached_file_info = capture_file_info()
|
252
|
+
@cached_wlan_info = wlan_info()
|
253
|
+
|
254
|
+
return {
|
255
|
+
:date => Time.now.to_i,
|
256
|
+
:hostname => @hostname,
|
257
|
+
:disk_size => @cached_disk_info[:size],
|
258
|
+
:disk_used => @cached_disk_info[:used],
|
259
|
+
:memory_size => @cached_memory_info[:size],
|
260
|
+
:memory_used => @cached_memory_info[:used],
|
261
|
+
:cpu_usage => @cached_cpu_info,
|
262
|
+
|
263
|
+
:state => @state,
|
264
|
+
:filename => @filename,
|
265
|
+
:filesize => @cached_file_info[:filesize],
|
266
|
+
:duration => @cached_file_info[:duration],
|
267
|
+
|
268
|
+
:current_channel => @cached_wlan_info[:current_channel],
|
269
|
+
:channel_walk => @cached_wlan_info[:channel_walk],
|
270
|
+
:utilization => @cached_wlan_info[:utilization],
|
271
|
+
:utilization_channel => @cached_wlan_info[:utilization_channel],
|
272
|
+
}
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|