vncrec 1.0.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 +16 -0
- data/.rspec +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +36 -0
- data/Rakefile +8 -0
- data/bin/vncrec +80 -0
- data/examples/exit.rb +8 -0
- data/examples/mp4.rb +10 -0
- data/examples/mp4audio.rb +11 -0
- data/ext/enchex_c/ReadRect.c +215 -0
- data/ext/enchex_c/extconf.rb +5 -0
- data/lib/vncrec.rb +6 -0
- data/lib/vncrec/constants.rb +10 -0
- data/lib/vncrec/recorder.rb +286 -0
- data/lib/vncrec/rfb/enchex.rb +115 -0
- data/lib/vncrec/rfb/encraw.rb +33 -0
- data/lib/vncrec/rfb/enczrle.rb +202 -0
- data/lib/vncrec/rfb/proxy.rb +200 -0
- data/lib/vncrec/version.rb +3 -0
- data/lib/vncrec/writers.rb +209 -0
- data/spec/executable_spec.rb +40 -0
- data/spec/recorder_spec.rb +338 -0
- data/spec/spec_helper.rb +171 -0
- data/spec/writers_spec.rb +121 -0
- data/vncrec.gemspec +34 -0
- metadata +165 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'vncrec' do
|
4
|
+
describe 'command line options' do
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'sending signals', vnc: true, port: true do
|
8
|
+
let(:ps) { `ps -A`.lines.grep(@pid.to_s) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
@pid = fork do
|
12
|
+
exec 'vncrec'
|
13
|
+
end
|
14
|
+
sleep 1
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when connected' do
|
18
|
+
before { launch_vnc_server 5900 }
|
19
|
+
|
20
|
+
it 'INT stops' do
|
21
|
+
expect_no_timeout(3) do
|
22
|
+
Process.kill('INT', @pid)
|
23
|
+
Process.waitpid(@pid)
|
24
|
+
end
|
25
|
+
expect($?.exited?).to be_truthy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when disconnected' do
|
30
|
+
|
31
|
+
it 'INT stops' do
|
32
|
+
expect_no_timeout(3) do
|
33
|
+
Process.kill('INT', @pid)
|
34
|
+
Process.waitpid(@pid)
|
35
|
+
end
|
36
|
+
expect($?.exited?).to be_truthy
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,338 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
Rec = VNCRec::Recorder
|
3
|
+
|
4
|
+
describe Rec do
|
5
|
+
describe '#intialize' do
|
6
|
+
let(:width) { r.instance_variable_get(:@w) }
|
7
|
+
let(:height) { r.instance_variable_get(:@h) }
|
8
|
+
|
9
|
+
describe 'option geometry:' do
|
10
|
+
context 'passed with wrong format' do
|
11
|
+
it 'leads to fail' do
|
12
|
+
expect { Rec.new(geometry: '1920') }.to raise_error ArgumentError
|
13
|
+
expect { Rec.new(geometry: 1920) }.to raise_error ArgumentError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'passed and correct' do
|
18
|
+
let(:r) { Rec.new(geometry: '800x600') }
|
19
|
+
|
20
|
+
it 'raises no error' do
|
21
|
+
expect { r }.not_to raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'IVs are set' do
|
25
|
+
r
|
26
|
+
r.send(:parse_geometry)
|
27
|
+
expect(width).to eq(800)
|
28
|
+
expect(height).to eq(600)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'encoding' do
|
34
|
+
let(:iv) { rec.instance_variable_get(:@enc) }
|
35
|
+
|
36
|
+
context 'given raw' do
|
37
|
+
let(:rec) { Rec.new(encoding: VNCRec::ENC_RAW) }
|
38
|
+
|
39
|
+
it ' sets iv to constant ENC_RAW' do
|
40
|
+
rec
|
41
|
+
expect(iv).to eq VNCRec::ENC_RAW
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'given zrle' do
|
46
|
+
let(:rec) { Rec.new(encoding: VNCRec::ENC_ZRLE) }
|
47
|
+
|
48
|
+
it ' sets iv to constant ENC_ZRLE' do
|
49
|
+
rec
|
50
|
+
expect(iv).to eq VNCRec::ENC_ZRLE
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'given hextile' do
|
55
|
+
let(:rec) { Rec.new(encoding: VNCRec::ENC_HEXTILE) }
|
56
|
+
|
57
|
+
it ' sets iv to constant ENC_HEXTILE' do
|
58
|
+
rec
|
59
|
+
expect(iv).to eq VNCRec::ENC_HEXTILE
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'option host:', port: true do
|
65
|
+
context 'when given' do
|
66
|
+
before do
|
67
|
+
@sock = nil
|
68
|
+
Thread.new do
|
69
|
+
@sock = TCPServer.new(5900).accept
|
70
|
+
end.run
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:rec) { Rec.new host: 'localhost', port: 5900 }
|
74
|
+
|
75
|
+
it 'rec tries to connect' do
|
76
|
+
expect do
|
77
|
+
rec.run
|
78
|
+
sleep 1
|
79
|
+
end.to change { @sock.nil? }
|
80
|
+
.from(true).to(false)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
context 'when not given' do
|
84
|
+
let(:rec) { Rec.new }
|
85
|
+
|
86
|
+
it 'rec listens on specified port' do
|
87
|
+
rec.run
|
88
|
+
sleep 1
|
89
|
+
expect(pid_using_port 5900).to eq Process.pid
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'option pixel format:' do
|
95
|
+
subject { rec.instance_variable_get(:@pix_fmt) }
|
96
|
+
context 'passing bgra' do
|
97
|
+
let(:rec) { Rec.new(filename: 'file.mp4', pix_fmt: 'bgra') }
|
98
|
+
|
99
|
+
it 'does not raise error' do
|
100
|
+
expect { rec }.not_to raise_error
|
101
|
+
end
|
102
|
+
it { is_expected.to eq VNCRec::PIX_FMT_BGRA }
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'passing bgr8' do
|
106
|
+
let(:rec) { Rec.new(filename: 'file.mp4', pix_fmt: 'bgr8') }
|
107
|
+
|
108
|
+
it 'does not raise error' do
|
109
|
+
expect { rec }.not_to raise_error
|
110
|
+
end
|
111
|
+
it { is_expected.to eq VNCRec::PIX_FMT_BGR8 }
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'passing other' do
|
115
|
+
let(:rec) { Rec.new(filename: 'file.mp4', pix_fmt: 'gbr32') }
|
116
|
+
|
117
|
+
it 'raises error' do
|
118
|
+
expect { rec }.to raise_error ArgumentError
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe 'option port:', port: true do
|
124
|
+
let(:rec) { Rec.new(port: port) }
|
125
|
+
context 'valid integer given' do
|
126
|
+
let(:port) { 5900 }
|
127
|
+
|
128
|
+
it 'does not raise error' do
|
129
|
+
expect { rec }.not_to raise_error
|
130
|
+
end
|
131
|
+
end
|
132
|
+
context 'integer too big' do
|
133
|
+
let(:port) { 10_000_000 }
|
134
|
+
it 'raises ArgumentError' do
|
135
|
+
expect { rec }.to raise_error ArgumentError
|
136
|
+
end
|
137
|
+
end
|
138
|
+
context 'integer too small' do
|
139
|
+
let(:port) { 0 }
|
140
|
+
it 'raises ArgumentError' do
|
141
|
+
expect { rec }.to raise_error ArgumentError
|
142
|
+
end
|
143
|
+
end
|
144
|
+
context 'non-integer' do
|
145
|
+
let(:port) { 'aa' }
|
146
|
+
it 'raises ArgumentError' do
|
147
|
+
expect { rec }.to raise_error ArgumentError
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe 'option filename:' do
|
153
|
+
let(:f1) { Rec.new }
|
154
|
+
let(:f2) { Rec.new(filename: 'file') }
|
155
|
+
let(:f3) { Rec.new(filename: '/root/file') }
|
156
|
+
|
157
|
+
context 'when no filename given' do
|
158
|
+
it 'file with default name exists' do
|
159
|
+
expect { f1 }.to change { File.exist?('5900.raw') }.from(false).to(true)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
context 'when given' do
|
163
|
+
it 'file with specified name exists' do
|
164
|
+
expect { f2 }.to change { File.exist?('file') }.from(false).to(true)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
context 'when can create file' do
|
168
|
+
it 'file exists' do
|
169
|
+
expect { f2 }.to change { File.exist?('file') }.from(false).to(true)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
context 'when can not create file' do
|
173
|
+
it 'raises error' do
|
174
|
+
expect { f3 }.to raise_error(/Cannot create file .*/)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
describe '#running?', port: true, vnc: true do
|
180
|
+
let(:rec) { Rec.new(filename: '/dev/null') }
|
181
|
+
context 'when not yet connected' do
|
182
|
+
it 'returns false' do
|
183
|
+
expect(rec.running?).to be_falsy
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'when connected' do
|
188
|
+
before { rec.run }
|
189
|
+
it 'returns true' do
|
190
|
+
launch_vnc_server 5900
|
191
|
+
expect(rec.running?).to be_truthy
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
describe 'network issues', port: true, vnc: true do
|
196
|
+
subject(:rec) { Rec.new(filename: 'file.raw', port: get_free_port) }
|
197
|
+
subject(:port) { rec.instance_variable_get(:@port) }
|
198
|
+
context 'when server is not responding' do
|
199
|
+
it 'exits eventually, correct close' do
|
200
|
+
end
|
201
|
+
end
|
202
|
+
context 'when connection is lost' do
|
203
|
+
after(:each) do
|
204
|
+
`killall x11vnc &>/dev/null`
|
205
|
+
end
|
206
|
+
it 'closes correctly', port: true do
|
207
|
+
rec.run
|
208
|
+
launch_vnc_server port
|
209
|
+
sleep 4
|
210
|
+
`killall x11vnc &>/dev/null`
|
211
|
+
sleep 1
|
212
|
+
expect(rec.stopped?).to be_truthy
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
describe 'basic functionality' do
|
217
|
+
describe 'Pix format', port: true, vnc: true do
|
218
|
+
before(:each) { `rm -f rec.jpg &>/dev/null` }
|
219
|
+
let(:port) { get_free_port }
|
220
|
+
let(:geo) { '800x600' }
|
221
|
+
let(:common_options) do
|
222
|
+
{
|
223
|
+
geometry: geo,
|
224
|
+
filename: 'file.raw',
|
225
|
+
encoding: VNCRec::ENC_RAW,
|
226
|
+
port: port
|
227
|
+
}
|
228
|
+
end
|
229
|
+
let(:rec) do
|
230
|
+
Rec.new(common_options.merge(pix_fmt: f))
|
231
|
+
end
|
232
|
+
let(:run) do
|
233
|
+
rec.run
|
234
|
+
sleep 5
|
235
|
+
rec.stop
|
236
|
+
end
|
237
|
+
|
238
|
+
context 'given bgr8' do
|
239
|
+
let(:f) { 'bgr8' }
|
240
|
+
|
241
|
+
it 'produces output' do
|
242
|
+
rec
|
243
|
+
launch_vnc_server port
|
244
|
+
run
|
245
|
+
sleep 1
|
246
|
+
`ffmpeg -y -f rawvideo -s #{geo} -r 3 -pix_fmt #{f} -i file.raw \
|
247
|
+
-frames 1 rec.jpg &>/dev/null`
|
248
|
+
sleep 4
|
249
|
+
expect(File.exist?('rec.jpg')).to be_truthy
|
250
|
+
end
|
251
|
+
|
252
|
+
context 'given bgra' do
|
253
|
+
let(:f) { 'bgra' }
|
254
|
+
|
255
|
+
it 'produces output' do
|
256
|
+
rec
|
257
|
+
launch_vnc_server port
|
258
|
+
run
|
259
|
+
sleep 1
|
260
|
+
`ffmpeg -y -f rawvideo -s #{geo} -r 3 -pix_fmt #{f} -i file.raw \
|
261
|
+
-frames 1 rec.jpg &>/dev/null`
|
262
|
+
sleep 4
|
263
|
+
expect(File.exist?('rec.jpg')).to be_truthy
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
describe 'Transmission mode', port: true, vnc: true do
|
270
|
+
context '(with resolution 800x600)' do
|
271
|
+
let(:geo) { '800x600' }
|
272
|
+
describe 'raw' do
|
273
|
+
let(:mode) { VNCRec::ENC_RAW }
|
274
|
+
let(:port) { get_free_port }
|
275
|
+
let(:rec) do
|
276
|
+
Rec.new(
|
277
|
+
geometry: geo,
|
278
|
+
filename: 'file.raw',
|
279
|
+
encoding: mode,
|
280
|
+
port: port
|
281
|
+
)
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'works' do
|
285
|
+
rec.run
|
286
|
+
launch_vnc_server port
|
287
|
+
sleep 3
|
288
|
+
rec.stop
|
289
|
+
sleep 1
|
290
|
+
expect(File.exist?('file.raw')).to be_truthy
|
291
|
+
expect(File.size('file.raw')).to be > 0
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe 'hextile', skip: 1 do
|
296
|
+
let(:mode) { VNCRec::ENC_HEXTILE }
|
297
|
+
let(:port) { get_free_port }
|
298
|
+
let(:rec) do
|
299
|
+
Rec.new(
|
300
|
+
geometry: geo,
|
301
|
+
filename: 'file.raw',
|
302
|
+
encoding: mode,
|
303
|
+
port: port
|
304
|
+
)
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'works' do
|
308
|
+
rec.run
|
309
|
+
launch_vnc_server port
|
310
|
+
sleep 3
|
311
|
+
rec.stop
|
312
|
+
sleep 1
|
313
|
+
expect(File.exist?('file.raw')).to be_truthy
|
314
|
+
expect(File.size('file.raw')).to be > 0
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
describe '#kill', port: true, vnc: true do
|
320
|
+
subject(:rec) { Rec.new(filename: 'file.mp4', port: get_free_port) }
|
321
|
+
subject(:port) { rec.instance_variable_get(:@port) }
|
322
|
+
before(:each) do
|
323
|
+
rec.run
|
324
|
+
launch_vnc_server port
|
325
|
+
sleep 6
|
326
|
+
end
|
327
|
+
it 'recorder is dead' do
|
328
|
+
ffmpeg = rec.instance_variable_get(:@file)
|
329
|
+
expect(rec.stopped?).to be_falsy
|
330
|
+
expect(ffmpeg.closed?).to be_falsy
|
331
|
+
rec.stop
|
332
|
+
sleep 0.5
|
333
|
+
expect(rec.stopped?).to be_truthy
|
334
|
+
expect(ffmpeg.closed?).to be_truthy
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'socket'
|
3
|
+
require 'vncrec'
|
4
|
+
require 'active_support/core_ext/numeric/time'
|
5
|
+
|
6
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
7
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
8
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
9
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
10
|
+
#
|
11
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
12
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
13
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
14
|
+
# individual file that may not need all of that loaded. Instead, make a
|
15
|
+
# separate helper file that requires this one and then use it only in the specs
|
16
|
+
# that actually need it.
|
17
|
+
#
|
18
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
19
|
+
# users commonly want.
|
20
|
+
#
|
21
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
22
|
+
RSpec.configure do |config|
|
23
|
+
config.exclusion_filter = :interactive, :skip
|
24
|
+
config.filter_run :focus
|
25
|
+
config.run_all_when_everything_filtered = true
|
26
|
+
|
27
|
+
config.before(:all) do
|
28
|
+
`rake gem`
|
29
|
+
end
|
30
|
+
|
31
|
+
config.before(:each, port: true) do
|
32
|
+
free_port 5900
|
33
|
+
end
|
34
|
+
|
35
|
+
config.after(:each, port: true) do
|
36
|
+
free_port 5900
|
37
|
+
end
|
38
|
+
|
39
|
+
config.after(:each) do
|
40
|
+
kill_children
|
41
|
+
`rake clean &>/dev/null`
|
42
|
+
`rm -rf file* &>/dev/null`
|
43
|
+
end
|
44
|
+
|
45
|
+
config.after(:each, vnc: true) do
|
46
|
+
stop_vnc_server
|
47
|
+
end
|
48
|
+
|
49
|
+
config.after(:all) do
|
50
|
+
`rake clean`
|
51
|
+
`kill -ABRT \`pgrep x11vnc\` &>/dev/null`
|
52
|
+
begin
|
53
|
+
`kill -INT \`pgrep ruby\` \`pgrep vncrec\` &>/dev/null`
|
54
|
+
kill_children
|
55
|
+
rescue SignalException
|
56
|
+
end
|
57
|
+
`xset r &>/dev/null`
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_free_port
|
62
|
+
gen = proc do
|
63
|
+
(rand * 1000).to_i + 5001
|
64
|
+
end
|
65
|
+
port = gen.call
|
66
|
+
port = gen.call while port_in_use? port
|
67
|
+
port
|
68
|
+
end
|
69
|
+
|
70
|
+
def port_avail?(port)
|
71
|
+
lines = File.readlines('/proc/net/tcp')
|
72
|
+
rx = /\d+: \d+:(?<port>\w+)/
|
73
|
+
!lines.map do |line|
|
74
|
+
m = line.match(rx)
|
75
|
+
m[:port].to_i(16) if m
|
76
|
+
end.include?(port)
|
77
|
+
end
|
78
|
+
|
79
|
+
def port_in_use?(port)
|
80
|
+
!port_avail?(port)
|
81
|
+
end
|
82
|
+
|
83
|
+
def pid_using_port(port)
|
84
|
+
lines = `lsof -i:#{port}`.lines
|
85
|
+
return nil if lines.empty?
|
86
|
+
col = lines[0].split(' ').index('PID')
|
87
|
+
lines.drop(1)[0].split(' ')[col].to_i
|
88
|
+
end
|
89
|
+
|
90
|
+
def free_port(port)
|
91
|
+
p = pid_using_port(port)
|
92
|
+
if Process.pid == p
|
93
|
+
close_socket_descriptors
|
94
|
+
return
|
95
|
+
end
|
96
|
+
`kill -INT #{p} &>/dev/null; sleep 1`
|
97
|
+
return unless port_in_use?(port)
|
98
|
+
`kill -QUIT #{p} &>/dev/null; sleep 1`
|
99
|
+
return unless port_in_use?(port)
|
100
|
+
`kill -KILL #{p} &>/dev/null; sleep 1`
|
101
|
+
end
|
102
|
+
|
103
|
+
def kill_children(sig = 'INT')
|
104
|
+
chids = `ps --ppid #{Process.pid} 2>/dev/null`
|
105
|
+
.lines.drop(1).map { |l| l.split(/\W+/)[0] }
|
106
|
+
chids.each do |v|
|
107
|
+
`kill -#{sig} #{v} 2>/dev/null`
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def close_socket_descriptors
|
112
|
+
ObjectSpace.each_object(TCPSocket) do |o|
|
113
|
+
begin
|
114
|
+
o.close unless o.closed?
|
115
|
+
rescue
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def read_process_timeout(cmd = '', tout = 0.2)
|
121
|
+
`rm -f log`
|
122
|
+
|
123
|
+
pid = fork do
|
124
|
+
exec "#{cmd} &>log"
|
125
|
+
end
|
126
|
+
|
127
|
+
sleep 2
|
128
|
+
`kill -INT #{pid}`
|
129
|
+
sleep 0.3
|
130
|
+
|
131
|
+
begin
|
132
|
+
Timeout.timeout(0.5) do
|
133
|
+
Process.waitpid(pid)
|
134
|
+
end
|
135
|
+
rescue Timeout::Error
|
136
|
+
`kill -TERM #{pid}`
|
137
|
+
end
|
138
|
+
|
139
|
+
f = File.open('log', 'r')
|
140
|
+
res = f.read_nonblock(1024)
|
141
|
+
f.close
|
142
|
+
`rm -f log`
|
143
|
+
res
|
144
|
+
end
|
145
|
+
|
146
|
+
def expect_timeout(time, &block)
|
147
|
+
expect do
|
148
|
+
Timeout.timeout(time) do
|
149
|
+
block.call
|
150
|
+
end
|
151
|
+
end.to raise_error Timeout::Error
|
152
|
+
end
|
153
|
+
|
154
|
+
def expect_no_timeout(time, &block)
|
155
|
+
expect do
|
156
|
+
Timeout.timeout(time) do
|
157
|
+
block.call
|
158
|
+
end
|
159
|
+
end.not_to raise_error
|
160
|
+
end
|
161
|
+
|
162
|
+
def launch_vnc_server(port, host = '0.0.0.0', debug = false)
|
163
|
+
fork do
|
164
|
+
`x11vnc --connect #{host}:#{port} #{debug ? nil : '&>/dev/null'}`
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def stop_vnc_server
|
169
|
+
`killall x11vnc &>/dev/null`
|
170
|
+
'killall -ABRT x11vnc &>/dev/null'
|
171
|
+
end
|