ronin-post_ex 0.1.0.beta1
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/.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
|