tochka 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +64 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/exe/tochka-miniui +36 -0
- data/exe/tochkad +44 -0
- data/ext/tochka-miniui +62 -0
- data/ext/tochkad +62 -0
- data/lib/tochka/agent.rb +116 -0
- data/lib/tochka/athsurvey.rb +109 -0
- data/lib/tochka/channel.rb +39 -0
- data/lib/tochka/command/tochka-miniui.rb +470 -0
- data/lib/tochka/command/tochkad.rb +209 -0
- data/lib/tochka/log.rb +38 -0
- data/lib/tochka/pitft_button.rb +105 -0
- data/lib/tochka/version.rb +3 -0
- data/lib/tochka/wlan.rb +113 -0
- data/lib/tochka.rb +11 -0
- data/tochka.gemspec +35 -0
- metadata +152 -0
@@ -0,0 +1,470 @@
|
|
1
|
+
module Tochka
|
2
|
+
require "sdl"
|
3
|
+
require "socket"
|
4
|
+
require "pidfile"
|
5
|
+
|
6
|
+
class TochkaMiniUI
|
7
|
+
DEBUG=true
|
8
|
+
|
9
|
+
DEFAULT_FONT_PATH="/usr/share/fonts/truetype/freefont/FreeSansBold.ttf"
|
10
|
+
DEFAULT_LOG_FILE="/var/log/tochka-miniui.log"
|
11
|
+
|
12
|
+
WIDTH=320
|
13
|
+
HEIGHT=240
|
14
|
+
COLOR=24
|
15
|
+
|
16
|
+
CAPTION="TochikaMini"
|
17
|
+
|
18
|
+
COLOR_RED=:red
|
19
|
+
COLOR_GREEN=:green
|
20
|
+
COLOR_BLUE=:blue
|
21
|
+
COLOR_WHITE=:white
|
22
|
+
COLOR_BLACK=:black
|
23
|
+
|
24
|
+
LEVEL1_BASE_X = 15
|
25
|
+
LEVEL1_BASE_Y = 5
|
26
|
+
LEVEL1_BASE_X_C2 = 160
|
27
|
+
|
28
|
+
VAR_OFFSET_X = 2
|
29
|
+
VAR_OFFSET_Y = 14
|
30
|
+
|
31
|
+
OFFSET_X = 110
|
32
|
+
OFFSET_Y = 35
|
33
|
+
LEVEL2_BASE_X = LEVEL1_BASE_X
|
34
|
+
LEVEL2_BASE_Y = LEVEL1_BASE_Y + OFFSET_Y
|
35
|
+
|
36
|
+
LEVEL3_BASE_X = LEVEL1_BASE_X
|
37
|
+
LEVEL3_BASE_Y = 190
|
38
|
+
|
39
|
+
LEVEL4_BASE_X = 0
|
40
|
+
LEVEL4_BASE_Y = HEIGHT-VAR_OFFSET_Y
|
41
|
+
|
42
|
+
def self.default_options
|
43
|
+
return {
|
44
|
+
:font_path => DEFAULT_FONT_PATH,
|
45
|
+
:log_file => DEFAULT_LOG_FILE,
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize font_path
|
50
|
+
@font_path = font_path
|
51
|
+
check_priviledge() # need to be root
|
52
|
+
init_sdl()
|
53
|
+
init_var()
|
54
|
+
|
55
|
+
begin
|
56
|
+
@pf = PidFile.new(:piddir=> "/var/run", :pidfile => "tochka-miniui.pid")
|
57
|
+
rescue => e
|
58
|
+
$log.err("pid file is in trouble (#{e})")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def draw_lattice
|
63
|
+
@screen.draw_line(9, 35, 306 + 9, 35, @colors[COLOR_WHITE], true)
|
64
|
+
@screen.draw_line(9, 185, 306 + 9, 185, @colors[COLOR_WHITE], true)
|
65
|
+
@screen.draw_line(9, 225, 306 + 9, 225, @colors[COLOR_WHITE], true)
|
66
|
+
end
|
67
|
+
|
68
|
+
def draw_base_text
|
69
|
+
draw_base_text_level1
|
70
|
+
draw_base_text_level2
|
71
|
+
draw_base_text_level3
|
72
|
+
draw_base_text_level4
|
73
|
+
end
|
74
|
+
|
75
|
+
def draw_text level, idx, text, color=COLOR_WHITE
|
76
|
+
@font.draw_solid_utf8(@screen, text.to_s, *get_text_pos(level, idx),
|
77
|
+
255, 255, 255)
|
78
|
+
end
|
79
|
+
|
80
|
+
def draw_base_text_level1
|
81
|
+
draw_text(1, 0, "Date")
|
82
|
+
draw_text(1, 1, "Host Name")
|
83
|
+
end
|
84
|
+
|
85
|
+
def draw_base_text_level3
|
86
|
+
draw_text(3, 0, "Disk Usage")
|
87
|
+
draw_text(3, 1, "Memory Usage")
|
88
|
+
draw_text(3, 2, "CPU Usage")
|
89
|
+
end
|
90
|
+
|
91
|
+
def draw_base_text_level2
|
92
|
+
draw_text(2, 0, "State")
|
93
|
+
draw_text(2, 1, "File Name")
|
94
|
+
draw_text(2, 2, "File Size")
|
95
|
+
draw_text(2, 3, "Duration")
|
96
|
+
draw_text(2, 4, "Current Channel")
|
97
|
+
draw_text(2, 5, "Channel Walk")
|
98
|
+
draw_text(2, 6, "Size per sec")
|
99
|
+
draw_text(2, 7, "Utilization")
|
100
|
+
end
|
101
|
+
|
102
|
+
def draw_base_text_level4
|
103
|
+
@font.draw_solid_utf8(@screen, "START | STOP | MODE1 | MODE2",
|
104
|
+
LEVEL4_BASE_X, LEVEL4_BASE_Y, 255, 255, 255)
|
105
|
+
@font.draw_solid_utf8(@screen, "stop count => #{@stop_count}",
|
106
|
+
LEVEL4_BASE_X + 200, LEVEL4_BASE_Y, 255, 0, 0)
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_level1_pos idx
|
110
|
+
# 0 1
|
111
|
+
offset_x = LEVEL1_BASE_X
|
112
|
+
offset_y = 0
|
113
|
+
if idx % 2 == 1
|
114
|
+
offset_x = 160
|
115
|
+
end
|
116
|
+
offset_y = LEVEL1_BASE_Y
|
117
|
+
return [offset_x, offset_y]
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_level2_pos idx
|
121
|
+
# 0 1
|
122
|
+
# 2 3
|
123
|
+
# 4 5
|
124
|
+
# 6 7
|
125
|
+
offset_x = LEVEL1_BASE_X
|
126
|
+
offset_y = 0
|
127
|
+
if idx % 2 == 1
|
128
|
+
offset_x += OFFSET_X
|
129
|
+
end
|
130
|
+
offset_y = LEVEL2_BASE_Y + OFFSET_Y * (idx / 2)
|
131
|
+
return [offset_x, offset_y]
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_level3_pos idx
|
135
|
+
# 0 1 2
|
136
|
+
offset_x = LEVEL3_BASE_X + 100 * (idx % 3)
|
137
|
+
offset_y = LEVEL3_BASE_Y
|
138
|
+
return [offset_x, offset_y]
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_text_pos level, idx
|
142
|
+
case level
|
143
|
+
when 1
|
144
|
+
return get_level1_pos(idx)
|
145
|
+
when 2
|
146
|
+
return get_level2_pos(idx)
|
147
|
+
when 3
|
148
|
+
return get_level3_pos(idx)
|
149
|
+
else
|
150
|
+
raise "idx #{idx} is invalid"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def get_level1_var_pos idx
|
155
|
+
x, y = get_level1_pos(idx)
|
156
|
+
return [x + VAR_OFFSET_X, y + VAR_OFFSET_Y]
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_level2_var_pos idx
|
160
|
+
x, y = get_level2_pos(idx)
|
161
|
+
return [x + VAR_OFFSET_X, y + VAR_OFFSET_Y]
|
162
|
+
end
|
163
|
+
|
164
|
+
def get_level3_var_pos idx
|
165
|
+
x, y = get_level3_pos(idx)
|
166
|
+
return [x + VAR_OFFSET_X, y + VAR_OFFSET_Y]
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_var_pos level, idx
|
170
|
+
case level
|
171
|
+
when 1
|
172
|
+
return get_level1_var_pos(idx)
|
173
|
+
when 2
|
174
|
+
return get_level2_var_pos(idx)
|
175
|
+
when 3
|
176
|
+
return get_level3_var_pos(idx)
|
177
|
+
else
|
178
|
+
raise "idx #{idx} is invalid"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def draw_var level, idx, var, color=COLOR_GREEN
|
183
|
+
@font.draw_solid_utf8(@screen, var.to_s, *get_var_pos(level, idx),
|
184
|
+
0, 255, 0)
|
185
|
+
end
|
186
|
+
|
187
|
+
def draw_all
|
188
|
+
#back ground
|
189
|
+
@screen.fill_rect(0, 0, WIDTH, HEIGHT, @colors[COLOR_BLACK])
|
190
|
+
|
191
|
+
draw_lattice
|
192
|
+
draw_base_text
|
193
|
+
|
194
|
+
# update text
|
195
|
+
draw_var(1, 0, @date)
|
196
|
+
draw_var(1, 1, @hostname)
|
197
|
+
|
198
|
+
draw_var(2, 0, @state)
|
199
|
+
draw_var(2, 1, @file_name)
|
200
|
+
draw_var(2, 2, file_size_to_h(@file_size))
|
201
|
+
draw_var(2, 3, duration_to_h(@duration))
|
202
|
+
draw_var(2, 4, @current_channel)
|
203
|
+
draw_var(2, 5, @channel_walk)
|
204
|
+
draw_var(2, 6, file_size_to_h(@size_per_sec))
|
205
|
+
draw_var(2, 7, "#{@utilization}% (#{@utilization_channel}ch)")
|
206
|
+
|
207
|
+
draw_var(3, 0, "#{@disk_usage}GB (#{@disk_usage_perc}%)")
|
208
|
+
draw_var(3, 1, "#{@mem_usage}MB (#{@mem_usage_perc}%)")
|
209
|
+
draw_var(3, 2, "#{@cpu_usage_perc}%")
|
210
|
+
end
|
211
|
+
|
212
|
+
def run
|
213
|
+
draw_all
|
214
|
+
# background
|
215
|
+
@screen.fill_rect(0, 0, WIDTH, HEIGHT, @colors[COLOR_BLACK])
|
216
|
+
draw_lattice
|
217
|
+
draw_base_text
|
218
|
+
prev = Time.now.to_i
|
219
|
+
|
220
|
+
while true
|
221
|
+
sleep 0.05
|
222
|
+
|
223
|
+
while event = SDL::Event.poll
|
224
|
+
end
|
225
|
+
|
226
|
+
buttons = @pitft_button.button_all_edge()
|
227
|
+
if buttons[0] == true
|
228
|
+
@ca.start_capture()
|
229
|
+
elsif buttons[1] == true
|
230
|
+
case @stop_count
|
231
|
+
when 0
|
232
|
+
$log.info("TOCHKA-UI: stop stage 1")
|
233
|
+
@stop_count = 1
|
234
|
+
when 3
|
235
|
+
$log.info("TOCHKA-UI: stop stage done, activate halt")
|
236
|
+
@ca.stop_capture
|
237
|
+
@stop_count = 0
|
238
|
+
else
|
239
|
+
@stop_count = 0
|
240
|
+
end
|
241
|
+
elsif buttons[2] == true
|
242
|
+
case @stop_count
|
243
|
+
when 1
|
244
|
+
$log.info("TOCHKA-UI: stop stage 2")
|
245
|
+
@stop_count = 2
|
246
|
+
else
|
247
|
+
@stop_count = 0
|
248
|
+
@pitft_button.backlight_off()
|
249
|
+
end
|
250
|
+
elsif buttons[3] == true
|
251
|
+
case @stop_count
|
252
|
+
when 2
|
253
|
+
$log.info("TOCHKA-UI: stop stage 3")
|
254
|
+
@stop_count = 3
|
255
|
+
else
|
256
|
+
@stop_count = 0
|
257
|
+
@pitft_button.backlight_on()
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
now = Time.now.to_i
|
262
|
+
next if prev == now
|
263
|
+
prev = now
|
264
|
+
|
265
|
+
update_var
|
266
|
+
|
267
|
+
draw_all
|
268
|
+
@screen.update_rect(0, 0, 0, 0)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
def check_priviledge
|
274
|
+
return false if DEBUG
|
275
|
+
raise "Must run as root" unless Process.uid == 0
|
276
|
+
return true
|
277
|
+
end
|
278
|
+
|
279
|
+
def init_sdl
|
280
|
+
SDL.putenv("SDL_VIDEODRIVER=fbdev")
|
281
|
+
SDL.putenv("SDL_FBDEV=/dev/fb1")
|
282
|
+
SDL.putenv("SDL_MOUSEDEV=/dev/input/touchscreen")
|
283
|
+
SDL.putenv("SDL_MOUSEDRV=TSLIB")
|
284
|
+
|
285
|
+
|
286
|
+
SDL.init(SDL::INIT_VIDEO)
|
287
|
+
@screen = SDL::Screen.open(WIDTH, HEIGHT, COLOR, SDL::SWSURFACE)
|
288
|
+
@surface = SDL::Surface.new(SDL::SWSURFACE, WIDTH, HEIGHT, @screen)
|
289
|
+
|
290
|
+
@colors = {
|
291
|
+
COLOR_RED => @screen.format.map_rgb(255, 0, 0),
|
292
|
+
COLOR_GREEN => @screen.format.map_rgb(0, 255, 0),
|
293
|
+
COLOR_BLUE => @screen.format.map_rgb(0, 0, 255),
|
294
|
+
COLOR_BLACK => @screen.format.map_rgb(0, 0, 0),
|
295
|
+
COLOR_WHITE => @screen.format.map_rgb(255, 255, 255),
|
296
|
+
}
|
297
|
+
|
298
|
+
SDL::WM::set_caption CAPTION, CAPTION
|
299
|
+
|
300
|
+
SDL::TTF::init
|
301
|
+
@font = SDL::TTF::open(@font_path, 12)
|
302
|
+
|
303
|
+
@pitft_button = PiTFTButton.new
|
304
|
+
@stop_count = 0
|
305
|
+
end
|
306
|
+
|
307
|
+
def init_var
|
308
|
+
@ca = Tochka::Agent.new
|
309
|
+
|
310
|
+
@date = get_date_str
|
311
|
+
@hostname = get_hostname
|
312
|
+
@disk_usage = 0
|
313
|
+
@disk_usage_perc = 0
|
314
|
+
@mem_usage = 0
|
315
|
+
@mem_usage_perc = 0
|
316
|
+
@cpu_usage_perc = 0
|
317
|
+
@size_per_sec = 0
|
318
|
+
@captured_pid = -1
|
319
|
+
|
320
|
+
# from agent
|
321
|
+
@state = @ca.state
|
322
|
+
@file_name = @ca.file_name
|
323
|
+
@file_size = @ca.file_size
|
324
|
+
@duration = @ca.duration
|
325
|
+
@current_channel = @ca.current_channel
|
326
|
+
@channel_walk = @ca.channel_walk
|
327
|
+
@frame_count = @ca.frame_count
|
328
|
+
@utilization = @ca.utilization
|
329
|
+
@utilization_channel = @ca.utilization_channel
|
330
|
+
|
331
|
+
@last_update_time = Time.now.to_i
|
332
|
+
@last_full_update_time = Time.now.to_i
|
333
|
+
end
|
334
|
+
|
335
|
+
def update_var
|
336
|
+
now = Time.now.to_i
|
337
|
+
if now == @last_update_time # too soon
|
338
|
+
return
|
339
|
+
end
|
340
|
+
|
341
|
+
# light update
|
342
|
+
@date = get_date_str
|
343
|
+
@last_update_time = now
|
344
|
+
|
345
|
+
if now < (@last_full_update_time + 1)
|
346
|
+
return
|
347
|
+
end
|
348
|
+
|
349
|
+
# heavy update
|
350
|
+
update_var_host
|
351
|
+
update_var_agent
|
352
|
+
@last_full_update_time = now
|
353
|
+
end
|
354
|
+
|
355
|
+
def update_var_host
|
356
|
+
@hostname = get_hostname
|
357
|
+
# disk
|
358
|
+
@disk_usage, @disk_usage_perc = get_disk_usage()
|
359
|
+
@mem_usage, @mem_usage_perc = get_mem_usage()
|
360
|
+
@cpu_usage_perc = get_cpu_usage()
|
361
|
+
end
|
362
|
+
|
363
|
+
def update_var_agent
|
364
|
+
unless @ca.get_status
|
365
|
+
$log.err("TOCHKA-UI: upsate failed")
|
366
|
+
end
|
367
|
+
@state = @ca.state
|
368
|
+
@file_name = @ca.file_name
|
369
|
+
@file_size = @ca.file_size
|
370
|
+
@duration = @ca.duration
|
371
|
+
@current_channel = @ca.current_channel
|
372
|
+
@channel_walk = @ca.channel_walk
|
373
|
+
@frame_count = @ca.frame_count
|
374
|
+
@size_per_sec = @file_size / @duration if @duration != 0
|
375
|
+
@utilization = @ca.utilization
|
376
|
+
@utilization_channel = @ca.utilization_channel
|
377
|
+
end
|
378
|
+
|
379
|
+
def get_date_str
|
380
|
+
Time.now.strftime("%Y/%m/%d %H:%M:%S")
|
381
|
+
end
|
382
|
+
|
383
|
+
def get_hostname
|
384
|
+
Socket.gethostname
|
385
|
+
end
|
386
|
+
|
387
|
+
def get_mem_usage
|
388
|
+
total = 0
|
389
|
+
free = 0
|
390
|
+
File.open("/proc/meminfo") do |file|
|
391
|
+
total = file.gets.split[1].to_i
|
392
|
+
free = file.gets.split[1].to_i
|
393
|
+
end
|
394
|
+
|
395
|
+
used = total - free
|
396
|
+
used_perc = used * 100 / total
|
397
|
+
return [used/1000, used_perc]
|
398
|
+
end
|
399
|
+
|
400
|
+
def get_cpu_usage
|
401
|
+
current = []
|
402
|
+
File.open("/proc/stat") do |file|
|
403
|
+
current = file.gets.split[1..4].map{|elm| elm.to_i}
|
404
|
+
end
|
405
|
+
if @prev_cpu == nil
|
406
|
+
@prev_cpu = current
|
407
|
+
return 0
|
408
|
+
end
|
409
|
+
|
410
|
+
usage_sub = current[0..2].inject(0){|sum, elm| sum += elm} -
|
411
|
+
@prev_cpu[0..2].inject(0){|sum, elm| sum += elm}
|
412
|
+
total_sub = current.inject(0){|sum, elm| sum += elm} -
|
413
|
+
@prev_cpu.inject(0){|sum, elm| sum += elm}
|
414
|
+
|
415
|
+
@prev_cpu = current
|
416
|
+
return ((usage_sub * 100) / total_sub)
|
417
|
+
end
|
418
|
+
|
419
|
+
def get_disk_usage
|
420
|
+
line = `df -h`.split("\n")[1].split
|
421
|
+
used_str = line[2]
|
422
|
+
perc_str = line[4]
|
423
|
+
|
424
|
+
match = used_str.match(/^([1-9\.]+)([GMK])(i|)$/)
|
425
|
+
unless match
|
426
|
+
raise "failed to get disk usage"
|
427
|
+
end
|
428
|
+
|
429
|
+
used = match[1].to_f
|
430
|
+
used = used / 1000 if match[2] == "M"
|
431
|
+
used = used / 1000 / 1000 if match[2] == "K"
|
432
|
+
|
433
|
+
perc = perc_str.to_i
|
434
|
+
|
435
|
+
return [used, perc]
|
436
|
+
rescue => e
|
437
|
+
$log.err("TOCHKA-UI: failed to acquire disk usage (#{e})")
|
438
|
+
return [0, 0]
|
439
|
+
end
|
440
|
+
|
441
|
+
def file_size_to_h bytes
|
442
|
+
return "0 B" if bytes == 0
|
443
|
+
kb = (bytes.to_f / 1000)
|
444
|
+
mb = kb.to_f / 1000
|
445
|
+
if mb < 1.0
|
446
|
+
return "#{(kb * 100).to_i.to_f/100} KB"
|
447
|
+
end
|
448
|
+
return "#{(mb * 100).to_i.to_f/100} MB"
|
449
|
+
end
|
450
|
+
|
451
|
+
def duration_to_h sec
|
452
|
+
return "0s" if sec == 0
|
453
|
+
time = sec
|
454
|
+
d_sec = sec % 60
|
455
|
+
time = time / 60
|
456
|
+
d_min = time % 60
|
457
|
+
time = time / 60
|
458
|
+
d_hour = time % 24
|
459
|
+
d_day = time / 24
|
460
|
+
|
461
|
+
str = ""
|
462
|
+
str += "#{d_day}d " if d_day > 0
|
463
|
+
str += "#{d_hour}h " if d_hour > 0
|
464
|
+
str += "#{d_min}m " if d_min > 0
|
465
|
+
str += "#{d_sec}s" if d_sec > 0
|
466
|
+
|
467
|
+
return str
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
module Tochka
|
2
|
+
require "socket"
|
3
|
+
require "json"
|
4
|
+
require "thread"
|
5
|
+
require "pidfile"
|
6
|
+
|
7
|
+
require "tochka/channel"
|
8
|
+
require "tochka/wlan"
|
9
|
+
require "tochka/log"
|
10
|
+
|
11
|
+
class Daemon
|
12
|
+
DEFAULT_CAP_PATH="/cap"
|
13
|
+
DEFAULT_IFNAME="wlan0"
|
14
|
+
DEFAULT_LOG_FILE="/var/log/tochkad.log"
|
15
|
+
|
16
|
+
CMD_GET_STATUS="get_status"
|
17
|
+
CMD_START_CAPTURE="start_capture"
|
18
|
+
CMD_STOP_CAPTURE="stop_capture"
|
19
|
+
|
20
|
+
STATE_INIT="init"
|
21
|
+
STATE_RUNNING="running"
|
22
|
+
STATE_STOP="stop"
|
23
|
+
|
24
|
+
def self.default_options
|
25
|
+
return {
|
26
|
+
:ifname => DEFAULT_IFNAME,
|
27
|
+
:cap_path => DEFAULT_CAP_PATH,
|
28
|
+
:log_file => DEFAULT_LOG_FILE,
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize ifname=DEFAULT_IFNAME, cap_path=DEFAULT_CAP_PATH
|
33
|
+
@cap_path = cap_path || DEFAULT_CAP_PATH
|
34
|
+
@ifname = ifname || DEFAULT_IFNAME
|
35
|
+
|
36
|
+
check_requirements()
|
37
|
+
init_status()
|
38
|
+
|
39
|
+
@wlan = Tochka::Wlan.new(@ifname)
|
40
|
+
|
41
|
+
@th_capture = nil
|
42
|
+
@event_q = Queue.new
|
43
|
+
@start_time = 0
|
44
|
+
|
45
|
+
@mutex = Mutex.new
|
46
|
+
@cv = ConditionVariable.new
|
47
|
+
|
48
|
+
begin
|
49
|
+
@pf = PidFile.new(:piddir => "/var/run", :pidfile => "tochkad.pid")
|
50
|
+
rescue => e
|
51
|
+
$log.err("pid file is in trouble (#{e})")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def run
|
56
|
+
# start various connection
|
57
|
+
@unix_sock = Tochka::UnixSocketChannel.new(Proc.new {|msg|
|
58
|
+
recv_handler(msg)
|
59
|
+
})
|
60
|
+
@unix_sock.start
|
61
|
+
|
62
|
+
loop do
|
63
|
+
@mutex.synchronize {
|
64
|
+
@cv.wait(@mutex) if @event_q.empty?
|
65
|
+
event = @event_q.pop
|
66
|
+
$log.debug("received event (#{event})")
|
67
|
+
handle_event(event)
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def check_requirements
|
73
|
+
# root privilege
|
74
|
+
# tshark exists?
|
75
|
+
end
|
76
|
+
|
77
|
+
def init_status new_state=STATE_INIT
|
78
|
+
@state = new_state
|
79
|
+
@file_name = ""
|
80
|
+
end
|
81
|
+
|
82
|
+
def recv_handler msg
|
83
|
+
json = JSON.parse(msg)
|
84
|
+
|
85
|
+
resp = {}
|
86
|
+
|
87
|
+
case json["command"]
|
88
|
+
when CMD_GET_STATUS
|
89
|
+
resp = recv_get_status()
|
90
|
+
when CMD_START_CAPTURE
|
91
|
+
resp = recv_start_capture()
|
92
|
+
when CMD_STOP_CAPTURE
|
93
|
+
resp = recv_stop_capture()
|
94
|
+
else
|
95
|
+
$log.err("discarded unknown command (req='#{json['command']}')")
|
96
|
+
resp = {"error" => "unknown command"}
|
97
|
+
end
|
98
|
+
|
99
|
+
return JSON.dump(resp)
|
100
|
+
rescue => e
|
101
|
+
$log.err("recv_handler has unknown error (#{e})")
|
102
|
+
end
|
103
|
+
|
104
|
+
def recv_get_status
|
105
|
+
$log.debug("accepted command (get_status)")
|
106
|
+
return status_hash()
|
107
|
+
end
|
108
|
+
|
109
|
+
def recv_start_capture
|
110
|
+
$log.debug("accepted command (start_capture")
|
111
|
+
|
112
|
+
@mutex.synchronize {
|
113
|
+
@event_q.push(CMD_START_CAPTURE)
|
114
|
+
@cv.signal if @cv
|
115
|
+
$log.debug("requested defered start_capture")
|
116
|
+
}
|
117
|
+
|
118
|
+
return {"status" => "start capture enqueued"}
|
119
|
+
end
|
120
|
+
|
121
|
+
def recv_stop_capture
|
122
|
+
$log.debug("accepted command (stop_capture")
|
123
|
+
|
124
|
+
@mutex.synchronize {
|
125
|
+
@event_q.push(CMD_STOP_CAPTURE)
|
126
|
+
@cv.signal if @cv
|
127
|
+
$log.debug("requested defered stop_capture")
|
128
|
+
}
|
129
|
+
|
130
|
+
return {"status" => "stop capture enqueued"}
|
131
|
+
end
|
132
|
+
|
133
|
+
def status_hash
|
134
|
+
return {
|
135
|
+
"state" => @state,
|
136
|
+
"file_name" => @file_name,
|
137
|
+
"file_size" => @wlan.file_size,
|
138
|
+
"duration" => @wlan.duration,
|
139
|
+
"current_channel" => @wlan.current_channel,
|
140
|
+
"channel_walk" => @wlan.channel_walk,
|
141
|
+
"utilization" => @wlan.utilization,
|
142
|
+
"utilization_channel" => @wlan.utilization_channel,
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
def handle_event event
|
147
|
+
$log.debug("invoke defered event handler (event => #{event})")
|
148
|
+
case event
|
149
|
+
when CMD_START_CAPTURE
|
150
|
+
start_capture
|
151
|
+
when CMD_STOP_CAPTURE
|
152
|
+
stop_capture
|
153
|
+
else
|
154
|
+
$log.err("defered handler has unknown event (#{event})")
|
155
|
+
end
|
156
|
+
rescue => e
|
157
|
+
$log.err("defered handler detected unknown error (#{e})")
|
158
|
+
end
|
159
|
+
|
160
|
+
def start_capture
|
161
|
+
if @state == STATE_RUNNING and @th_capture
|
162
|
+
$log.info("discarded start_capture (already initiated)")
|
163
|
+
return # do nothing
|
164
|
+
end
|
165
|
+
init_status() # refresh
|
166
|
+
|
167
|
+
@state = STATE_RUNNING
|
168
|
+
do_start_capture()
|
169
|
+
return
|
170
|
+
end
|
171
|
+
|
172
|
+
def stop_capture
|
173
|
+
if @state != STATE_RUNNING
|
174
|
+
$log.info("discarded stop_capture (not running)")
|
175
|
+
return
|
176
|
+
end
|
177
|
+
|
178
|
+
do_stop_capture()
|
179
|
+
@state = STATE_STOP
|
180
|
+
return
|
181
|
+
end
|
182
|
+
|
183
|
+
def do_start_capture
|
184
|
+
@file_name = generate_new_filename()
|
185
|
+
|
186
|
+
$log.debug("invoke capture thread (file=#{@file_name})")
|
187
|
+
@th_capture = Thread.new do
|
188
|
+
file_path = "#{@cap_path}/#{@file_name}"
|
189
|
+
@wlan.run_capture(file_path) # block until stopped
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def do_stop_capture
|
194
|
+
@wlan.stop_capture
|
195
|
+
|
196
|
+
$log.debug("kill capture thread (#{@th_capture})")
|
197
|
+
@th_capture.kill if @th_capture
|
198
|
+
end
|
199
|
+
|
200
|
+
def generate_new_filename()
|
201
|
+
return "#{Time.now.strftime("%Y%m%d%H%m%S")}_#{@ifname}_#{$$}.pcapng"
|
202
|
+
end
|
203
|
+
|
204
|
+
def move_channel current
|
205
|
+
# this is shit
|
206
|
+
return (current + 1) % 13 + 1
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
data/lib/tochka/log.rb
ADDED
@@ -0,0 +1,38 @@
|
|
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.err(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
|
37
|
+
|
38
|
+
|