ronin-post_ex 0.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +6 -0
- data/.github/workflows/ruby.yml +31 -0
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/API_SPEC.md +235 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +23 -0
- data/Gemfile +36 -0
- data/README.md +245 -0
- data/Rakefile +34 -0
- data/examples/bind_shell.rb +19 -0
- data/gemspec.yml +25 -0
- data/lib/ronin/post_ex/cli/shell_shell.rb +66 -0
- data/lib/ronin/post_ex/cli/system_shell.rb +811 -0
- data/lib/ronin/post_ex/remote_dir.rb +190 -0
- data/lib/ronin/post_ex/remote_file/stat.rb +174 -0
- data/lib/ronin/post_ex/remote_file.rb +417 -0
- data/lib/ronin/post_ex/remote_process.rb +170 -0
- data/lib/ronin/post_ex/resource.rb +144 -0
- data/lib/ronin/post_ex/sessions/bind_shell.rb +60 -0
- data/lib/ronin/post_ex/sessions/remote_shell_session.rb +48 -0
- data/lib/ronin/post_ex/sessions/reverse_shell.rb +67 -0
- data/lib/ronin/post_ex/sessions/rpc_session.rb +779 -0
- data/lib/ronin/post_ex/sessions/session.rb +73 -0
- data/lib/ronin/post_ex/sessions/shell_session.rb +618 -0
- data/lib/ronin/post_ex/system/fs.rb +650 -0
- data/lib/ronin/post_ex/system/process.rb +422 -0
- data/lib/ronin/post_ex/system/shell.rb +1037 -0
- data/lib/ronin/post_ex/system.rb +191 -0
- data/lib/ronin/post_ex/version.rb +26 -0
- data/lib/ronin/post_ex.rb +22 -0
- data/ronin-post_ex.gemspec +61 -0
- data/spec/sessions/bind_shell_spec.rb +31 -0
- data/spec/sessions/remote_shell_session_spec.rb +28 -0
- data/spec/sessions/reverse_shell_spec.rb +49 -0
- data/spec/sessions/rpc_session_spec.rb +500 -0
- data/spec/sessions/session_spec.rb +61 -0
- data/spec/sessions/shell_session_spec.rb +482 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/system_spec.rb +66 -0
- metadata +155 -0
@@ -0,0 +1,417 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-post_ex - a Ruby API for Post-Exploitation.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-post_ex is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-post_ex is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-post_ex. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/post_ex/remote_file/stat'
|
22
|
+
require 'ronin/post_ex/resource'
|
23
|
+
|
24
|
+
require 'fake_io'
|
25
|
+
require 'set'
|
26
|
+
|
27
|
+
module Ronin
|
28
|
+
module PostEx
|
29
|
+
#
|
30
|
+
# The {RemoteFile} class represents files on a remote system. {RemoteFile}
|
31
|
+
# requires the API object to define either `file_read` and/or `file_write`.
|
32
|
+
# Additionally, {RemoteFile} can optionally use the `file_open`,
|
33
|
+
# `file_close`, `file_tell`, `file_seek` and `file_stat` methods.
|
34
|
+
#
|
35
|
+
# ## Supported API Methods
|
36
|
+
#
|
37
|
+
# * `file_open(path : String, mode : String) -> Integer`
|
38
|
+
# * `file_read(fd : Integer, length : Integer) -> String | nil`
|
39
|
+
# * `file_write(fd : Integer, pos : Integer, data : String) -> Integer`
|
40
|
+
# * `file_seek(fd : Integer, new_pos : Integer, whence : String)`
|
41
|
+
# * `file_tell(fd : Integer) -> Integer`
|
42
|
+
# * `file_ioctl(fd : Integer, command : String | Array[Integer], argument : Object) -> Integer`
|
43
|
+
# * `file_fcntl(fd : Integer, command : String | Array[Integer], argument : Object) -> Integer`
|
44
|
+
# * `file_stat(fd : Integer) => Hash[Symbol, Object] | nil`
|
45
|
+
|
46
|
+
# * `file_close(fd : Integer)`
|
47
|
+
# * `fs_readfile(path : String) -> String | nil`
|
48
|
+
# * `fs_stat(path : String) => Hash[Symbol, Object] | nil`
|
49
|
+
#
|
50
|
+
class RemoteFile < Resource
|
51
|
+
|
52
|
+
include FakeIO
|
53
|
+
|
54
|
+
#
|
55
|
+
# Creates a new remote controlled File object.
|
56
|
+
#
|
57
|
+
# @param [Sessions::Session#file_read, Sessions::Session#file_write] session
|
58
|
+
# The session object that defines the `file_read` and `file_write`
|
59
|
+
# methods.
|
60
|
+
#
|
61
|
+
# @param [String] path
|
62
|
+
# The path of the remote file.
|
63
|
+
#
|
64
|
+
# @param [String] mode
|
65
|
+
# The mode to open the file in.
|
66
|
+
#
|
67
|
+
# @note
|
68
|
+
# This method may use the `file_open` method, if it is defined by
|
69
|
+
# {#session}.
|
70
|
+
#
|
71
|
+
def initialize(session,path,mode='r')
|
72
|
+
@session = session
|
73
|
+
|
74
|
+
@path = path.to_s
|
75
|
+
@mode = mode.to_s
|
76
|
+
|
77
|
+
super()
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Opens a file.
|
82
|
+
#
|
83
|
+
# @param [Sessions::Session#file_read, Sessions::Session#file_write] session
|
84
|
+
# The session object controlling remote files.
|
85
|
+
#
|
86
|
+
# @param [String] path
|
87
|
+
# The path of the remote file.
|
88
|
+
#
|
89
|
+
# @yield [file]
|
90
|
+
# The given block will be passed the newly created file object.
|
91
|
+
# When the block has returned, the File object will be closed.
|
92
|
+
#
|
93
|
+
# @yieldparam [RemoteFile]
|
94
|
+
# The newly created file object.
|
95
|
+
#
|
96
|
+
# @return [RemoteFile, nil]
|
97
|
+
# If no block is given, then the newly opened remote file object will be
|
98
|
+
# returned. If a block was given, then `nil` will be returned.
|
99
|
+
#
|
100
|
+
def self.open(session,path)
|
101
|
+
io = new(session,path)
|
102
|
+
|
103
|
+
if block_given?
|
104
|
+
yield(io)
|
105
|
+
io.close
|
106
|
+
return
|
107
|
+
else
|
108
|
+
return io
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Seeks from beginning of file.
|
113
|
+
SEEK_SET = File::SEEK_SET
|
114
|
+
|
115
|
+
# Seeks from current position.
|
116
|
+
SEEK_CUR = File::SEEK_CUR
|
117
|
+
|
118
|
+
# Seeks from end of file.
|
119
|
+
SEEK_END = File::SEEK_END
|
120
|
+
|
121
|
+
# Seeks to next data.
|
122
|
+
SEEK_DATA = (defined?(File::SEEK_DATA) && File::SEEK_DATA) || 3
|
123
|
+
|
124
|
+
# Seeks to next hole.
|
125
|
+
SEEK_HOLE = (defined?(File::SEEK_HOLE) && File::SEEK_HOLE) || 4
|
126
|
+
|
127
|
+
# Mapping of `SEEK_*` constants to their String values.
|
128
|
+
#
|
129
|
+
# @api private
|
130
|
+
WHENCE = {
|
131
|
+
SEEK_SET => 'SEEK_SET',
|
132
|
+
SEEK_CUR => 'SEEK_CUR',
|
133
|
+
SEEK_END => 'SEEK_END',
|
134
|
+
SEEK_DATA => 'SEEK_DATA',
|
135
|
+
SEEK_HOLE => 'SEEK_HOLE'
|
136
|
+
}
|
137
|
+
|
138
|
+
#
|
139
|
+
# Sets the position in the file to read.
|
140
|
+
#
|
141
|
+
# @param [Integer] new_pos
|
142
|
+
# The new position to read from.
|
143
|
+
#
|
144
|
+
# @param [Integer] whence
|
145
|
+
# The origin point to seek from.
|
146
|
+
#
|
147
|
+
# @return [Integer]
|
148
|
+
# The new position within the file.
|
149
|
+
#
|
150
|
+
# @raise [ArgumentError]
|
151
|
+
# An invalid whence value was given.
|
152
|
+
#
|
153
|
+
# @note This method may use the `file_seek` API method, if it is defined
|
154
|
+
# by {#session}.
|
155
|
+
#
|
156
|
+
def seek(new_pos,whence=SEEK_SET)
|
157
|
+
clear_buffer!
|
158
|
+
|
159
|
+
unless WHENCE.has_key?(whence)
|
160
|
+
raise(ArgumentError,"invalid whence value: #{whence.inspect}")
|
161
|
+
end
|
162
|
+
|
163
|
+
if @session.respond_to?(:file_seek)
|
164
|
+
@session.file_seek(@fd,new_pos,WHENCE[whence])
|
165
|
+
end
|
166
|
+
|
167
|
+
@pos = new_pos
|
168
|
+
end
|
169
|
+
resource_method :seek, [:file_seek]
|
170
|
+
|
171
|
+
#
|
172
|
+
# The current offset in the file.
|
173
|
+
#
|
174
|
+
# @return [Integer]
|
175
|
+
# The current offset in bytes.
|
176
|
+
#
|
177
|
+
# @note
|
178
|
+
# This method may use the `file_tell` API method, if it is defined by
|
179
|
+
# {#session}.
|
180
|
+
#
|
181
|
+
def tell
|
182
|
+
if @session.respond_to?(:file_tell)
|
183
|
+
@pos = @session.file_tell(@fd)
|
184
|
+
else
|
185
|
+
@pos
|
186
|
+
end
|
187
|
+
end
|
188
|
+
resource_method :tell, [:file_tell]
|
189
|
+
|
190
|
+
#
|
191
|
+
# Executes a low-level command to control or query the IO stream.
|
192
|
+
#
|
193
|
+
# @param [String, Array<Integer>] command
|
194
|
+
# The IOCTL command.
|
195
|
+
#
|
196
|
+
# @param [Object] argument
|
197
|
+
# Argument of the command.
|
198
|
+
#
|
199
|
+
# @return [Integer]
|
200
|
+
# The return value from the `ioctl`.
|
201
|
+
#
|
202
|
+
# @raise [NotImplementedError]
|
203
|
+
# The API object does not define `file_ioctl`.
|
204
|
+
#
|
205
|
+
# @raise [RuntimeError]
|
206
|
+
# The `file_ioctl` method requires a file-descriptor.
|
207
|
+
#
|
208
|
+
# @note This method requires the `file_ioctl` API method.
|
209
|
+
#
|
210
|
+
def ioctl(command,argument)
|
211
|
+
unless @session.respond_to?(:file_ioctl)
|
212
|
+
raise(NotImplementedError,"#{@session.inspect} does not define file_ioctl")
|
213
|
+
end
|
214
|
+
|
215
|
+
if @fd == nil
|
216
|
+
raise(RuntimeError,"file_ioctl requires a file-descriptor")
|
217
|
+
end
|
218
|
+
|
219
|
+
return @session.file_ioctl(@fd,command,argument)
|
220
|
+
end
|
221
|
+
resource_method :ioctl, [:file_ioctl]
|
222
|
+
|
223
|
+
#
|
224
|
+
# Executes a low-level command to control or query the file stream.
|
225
|
+
#
|
226
|
+
# @param [String, Array<Integer>] command
|
227
|
+
# The FCNTL command.
|
228
|
+
#
|
229
|
+
# @param [Object] argument
|
230
|
+
# Argument of the command.
|
231
|
+
#
|
232
|
+
# @return [Integer]
|
233
|
+
# The return value from the `fcntl`.
|
234
|
+
#
|
235
|
+
# @raise [NotImplementedError]
|
236
|
+
# The API object does not define `file_fcntl`.
|
237
|
+
#
|
238
|
+
# @note This method requires the `file_fnctl` API method.
|
239
|
+
#
|
240
|
+
def fcntl(command,argument)
|
241
|
+
unless @session.respond_to?(:file_fcntl)
|
242
|
+
raise(NotImplementedError,"#{@session.inspect} does not define file_fcntl")
|
243
|
+
end
|
244
|
+
|
245
|
+
if @fd == nil
|
246
|
+
raise(RuntimeError,"file_ioctl requires a file-descriptor")
|
247
|
+
end
|
248
|
+
|
249
|
+
return @session.file_fcntl(@fd,command,argument)
|
250
|
+
end
|
251
|
+
resource_method :fcntl, [:file_fcntl]
|
252
|
+
|
253
|
+
#
|
254
|
+
# Re-opens the file.
|
255
|
+
#
|
256
|
+
# @param [String] path
|
257
|
+
# The new path for the file.
|
258
|
+
#
|
259
|
+
# @return [RemoteFile]
|
260
|
+
# The re-opened the file.
|
261
|
+
#
|
262
|
+
# @note
|
263
|
+
# This method may use the `file_close` and `file_open` API methods,
|
264
|
+
# if they are defined by {#session}.
|
265
|
+
#
|
266
|
+
def reopen(path)
|
267
|
+
close
|
268
|
+
|
269
|
+
@path = path.to_s
|
270
|
+
return open
|
271
|
+
end
|
272
|
+
resource_method :reopen, [:file_close, :file_open]
|
273
|
+
|
274
|
+
#
|
275
|
+
# The status information for the file.
|
276
|
+
#
|
277
|
+
# @return [Stat]
|
278
|
+
# The status information.
|
279
|
+
#
|
280
|
+
# @note This method relies on the `fs_stat` API method.
|
281
|
+
#
|
282
|
+
def stat
|
283
|
+
if @fd
|
284
|
+
Stat.new(@session, fd: @fd)
|
285
|
+
else
|
286
|
+
Stat.new(@session, path: @path)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
resource_method :stat, [:file_stat]
|
290
|
+
|
291
|
+
#
|
292
|
+
# Flushes the file.
|
293
|
+
#
|
294
|
+
# @return [self]
|
295
|
+
#
|
296
|
+
# @note This method may use the `file_flush` API method, if it is defined
|
297
|
+
# by {#session}.
|
298
|
+
#
|
299
|
+
def flush
|
300
|
+
if @session.respond_to?(:file_flush)
|
301
|
+
@session.file_flush
|
302
|
+
end
|
303
|
+
|
304
|
+
return self
|
305
|
+
end
|
306
|
+
|
307
|
+
#
|
308
|
+
# Flushes the file before closing it.
|
309
|
+
#
|
310
|
+
# @return [nil]
|
311
|
+
#
|
312
|
+
def close
|
313
|
+
flush if @mode.include?('w')
|
314
|
+
super()
|
315
|
+
end
|
316
|
+
|
317
|
+
#
|
318
|
+
# Inspects the open file.
|
319
|
+
#
|
320
|
+
# @return [String]
|
321
|
+
# The inspected open file.
|
322
|
+
#
|
323
|
+
def inspect
|
324
|
+
"#<#{self.class}:#{@path}>"
|
325
|
+
end
|
326
|
+
|
327
|
+
private
|
328
|
+
|
329
|
+
#
|
330
|
+
# Attempts calling `file_open` from the API object to open the remote
|
331
|
+
# file.
|
332
|
+
#
|
333
|
+
# @return [Object]
|
334
|
+
# The file descriptor returned by `file_open`.
|
335
|
+
#
|
336
|
+
# @note
|
337
|
+
# This method may use the `file_open` API method, if {#session} defines
|
338
|
+
# it.
|
339
|
+
#
|
340
|
+
def io_open
|
341
|
+
if @session.respond_to?(:file_open)
|
342
|
+
@session.file_open(@path,@mode)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
resource_method :open
|
346
|
+
|
347
|
+
# Default block size to read file data with.
|
348
|
+
BLOCK_SIZE = 4096
|
349
|
+
|
350
|
+
#
|
351
|
+
# Reads a block from the remote file by calling `file_read` or
|
352
|
+
# `fs_readfile` from the API object.
|
353
|
+
#
|
354
|
+
# @return [String, nil]
|
355
|
+
# A block of data from the file or `nil` if there is no more data to be
|
356
|
+
# read.
|
357
|
+
#
|
358
|
+
# @raise [IOError]
|
359
|
+
# The API object does not define `file_read` or `fs_readfile`.
|
360
|
+
#
|
361
|
+
# @note
|
362
|
+
# This method requires either the `fs_readfile` or `file_read` API
|
363
|
+
# methods.
|
364
|
+
#
|
365
|
+
def io_read
|
366
|
+
if @session.respond_to?(:file_read)
|
367
|
+
@session.file_read(@fd,BLOCK_SIZE)
|
368
|
+
elsif @api.respond_to?(:fs_readfile)
|
369
|
+
@eof = true
|
370
|
+
@api.fs_readfile(@path)
|
371
|
+
else
|
372
|
+
raise(IOError,"#{@session.inspect} does not support reading")
|
373
|
+
end
|
374
|
+
end
|
375
|
+
resource_method :read, [:file_read]
|
376
|
+
|
377
|
+
#
|
378
|
+
# Writes data to the remote file by calling `file_write` from the
|
379
|
+
# API object.
|
380
|
+
#
|
381
|
+
# @param [String] data
|
382
|
+
# The data to write.
|
383
|
+
#
|
384
|
+
# @return [Integer]
|
385
|
+
# The number of bytes writen.
|
386
|
+
#
|
387
|
+
# @raise [IOError]
|
388
|
+
# The API object does not define `file_write`.
|
389
|
+
#
|
390
|
+
# @note This method requires the `file_write` API method.
|
391
|
+
#
|
392
|
+
def io_write(data)
|
393
|
+
if @session.respond_to?(:file_write)
|
394
|
+
@pos += @session.file_write(@fd,@pos,data)
|
395
|
+
else
|
396
|
+
raise(IOError,"#{@session.inspect} does not support writing to files")
|
397
|
+
end
|
398
|
+
end
|
399
|
+
resource_method :write, [:file_write]
|
400
|
+
|
401
|
+
#
|
402
|
+
# Attempts calling `file_close` from the API object to close
|
403
|
+
# the file.
|
404
|
+
#
|
405
|
+
# @note This method may use the `file_close` method, if {#session} defines
|
406
|
+
# it.
|
407
|
+
#
|
408
|
+
def io_close
|
409
|
+
if @session.respond_to?(:file_close)
|
410
|
+
@session.file_close(@fd)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
resource_method :close
|
414
|
+
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-post_ex - a Ruby API for Post-Exploitation.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-post_ex is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-post_ex is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-post_ex. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/post_ex/resource'
|
22
|
+
|
23
|
+
require 'fake_io'
|
24
|
+
|
25
|
+
module Ronin
|
26
|
+
module PostEx
|
27
|
+
#
|
28
|
+
# The {RemoteProcess} class represents a command being executed on a remote
|
29
|
+
# system. The {RemoteProcess} class wraps around the `process_popen` and
|
30
|
+
# `process_read`, `process_write`, and `process_close` methods defined in
|
31
|
+
# the API object.
|
32
|
+
#
|
33
|
+
class RemoteProcess < Resource
|
34
|
+
|
35
|
+
include FakeIO
|
36
|
+
include Enumerable
|
37
|
+
|
38
|
+
# The command string.
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
attr_reader :command
|
42
|
+
|
43
|
+
#
|
44
|
+
# Creates a new remote process.
|
45
|
+
#
|
46
|
+
# @param [Sessions::Session] session
|
47
|
+
# The object controlling command execution.
|
48
|
+
#
|
49
|
+
# @param [String] command
|
50
|
+
# The command to run.
|
51
|
+
#
|
52
|
+
# @raise [NotImplementedError]
|
53
|
+
# The session object does not define `process_popen`.
|
54
|
+
#
|
55
|
+
def initialize(session,command)
|
56
|
+
unless session.respond_to?(:process_popen)
|
57
|
+
raise(NotImplementedError,"#{session.inspect} must define #process_popen for #{self.class}")
|
58
|
+
end
|
59
|
+
|
60
|
+
@session = session
|
61
|
+
@command = command
|
62
|
+
|
63
|
+
super()
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Reopens the command.
|
68
|
+
#
|
69
|
+
# @param [String] command
|
70
|
+
# The new command to run.
|
71
|
+
#
|
72
|
+
# @return [RemoteProcess]
|
73
|
+
# The new command.
|
74
|
+
#
|
75
|
+
def reopen(command)
|
76
|
+
close
|
77
|
+
|
78
|
+
@command = command
|
79
|
+
|
80
|
+
return open
|
81
|
+
end
|
82
|
+
resource_method :reopen, [:process_popen]
|
83
|
+
|
84
|
+
#
|
85
|
+
# Converts the command to a `String`.
|
86
|
+
#
|
87
|
+
# @return [String]
|
88
|
+
# The process'es command.
|
89
|
+
#
|
90
|
+
def to_s
|
91
|
+
@command
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Inspects the command.
|
96
|
+
#
|
97
|
+
# @return [String]
|
98
|
+
# The inspected command listing the program name and arguments.
|
99
|
+
#
|
100
|
+
def inspect
|
101
|
+
"#<#{self.class}: #{self}>"
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
#
|
107
|
+
# Executes and opens the command for reading.
|
108
|
+
#
|
109
|
+
# @return [Enumerator]
|
110
|
+
# The enumerator that wraps around `process_popen`.
|
111
|
+
#
|
112
|
+
def io_open
|
113
|
+
@session.enum_for(:process_popen,@command)
|
114
|
+
end
|
115
|
+
resource_method :open, [:process_popen]
|
116
|
+
|
117
|
+
# Default block size to read process output with.
|
118
|
+
BLOCK_SIZE = 4096
|
119
|
+
|
120
|
+
#
|
121
|
+
# Reads a line of output from the command.
|
122
|
+
#
|
123
|
+
# @return [String]
|
124
|
+
# A line of output.
|
125
|
+
#
|
126
|
+
# @raise [EOFError]
|
127
|
+
# The end of the output stream has been reached.
|
128
|
+
#
|
129
|
+
def io_read
|
130
|
+
if @session.respond_to?(:process_read)
|
131
|
+
@session.process_write(@fd,BLOCK_SIZE)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
resource_method :read
|
135
|
+
|
136
|
+
#
|
137
|
+
# Writes data to the shell.
|
138
|
+
#
|
139
|
+
# @param [String] data
|
140
|
+
# The data to write to the shell.
|
141
|
+
#
|
142
|
+
# @return [Integer]
|
143
|
+
# The number of bytes writen.
|
144
|
+
#
|
145
|
+
def io_write(data)
|
146
|
+
if @session.respond_to?(:process_write)
|
147
|
+
@session.process_write(@fd,data)
|
148
|
+
else
|
149
|
+
raise(IOError,"#{@session.inspect} does not support writing to the shell")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
resource_method :write, [:process_write]
|
153
|
+
|
154
|
+
#
|
155
|
+
# Attempts calling `process_close` from the API object to close
|
156
|
+
# the file.
|
157
|
+
#
|
158
|
+
# @note
|
159
|
+
# This method may use the `process_close` method, if {#session} defines it.
|
160
|
+
#
|
161
|
+
def io_close
|
162
|
+
if @session.respond_to?(:process_close)
|
163
|
+
@session.process_close(@fd)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
resource_method :close
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|