shells 0.1.23 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Shells
2
- VERSION = "0.1.23"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency 'rake', '~> 10.0'
28
28
  spec.add_development_dependency 'minitest', '~> 5.0'
29
29
  spec.add_development_dependency 'minitest-reporters'
30
+ spec.add_development_dependency 'rb-readline'
30
31
  spec.add_development_dependency 'pry'
31
32
  end
32
33
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shells
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.23
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Beau Barker
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-01 00:00:00.000000000 Z
11
+ date: 2018-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-ssh
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rb-readline
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: pry
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -122,15 +136,30 @@ files:
122
136
  - Rakefile
123
137
  - bin/console
124
138
  - bin/setup
139
+ - bin/test-client
125
140
  - lib/shells.rb
126
141
  - lib/shells/bash_common.rb
127
142
  - lib/shells/errors.rb
128
143
  - lib/shells/pf_sense_common.rb
129
- - lib/shells/pf_sense_serial_session.rb
130
- - lib/shells/pf_sense_ssh_session.rb
131
- - lib/shells/serial_session.rb
144
+ - lib/shells/pf_shell_wrapper.rb
145
+ - lib/shells/serial_bash_shell.rb
146
+ - lib/shells/serial_pf_sense_shell.rb
147
+ - lib/shells/serial_shell.rb
132
148
  - lib/shells/shell_base.rb
133
- - lib/shells/ssh_session.rb
149
+ - lib/shells/shell_base/debug.rb
150
+ - lib/shells/shell_base/exec.rb
151
+ - lib/shells/shell_base/hooks.rb
152
+ - lib/shells/shell_base/input.rb
153
+ - lib/shells/shell_base/interface.rb
154
+ - lib/shells/shell_base/options.rb
155
+ - lib/shells/shell_base/output.rb
156
+ - lib/shells/shell_base/prompt.rb
157
+ - lib/shells/shell_base/regex_escape.rb
158
+ - lib/shells/shell_base/run.rb
159
+ - lib/shells/shell_base/sync.rb
160
+ - lib/shells/ssh_bash_shell.rb
161
+ - lib/shells/ssh_pf_sense_shell.rb
162
+ - lib/shells/ssh_shell.rb
134
163
  - lib/shells/version.rb
135
164
  - shells.gemspec
136
165
  homepage: https://github.com/barkerest/shells
@@ -1,249 +0,0 @@
1
- require 'net/ssh'
2
- require 'shells/shell_base'
3
- require 'shells/bash_common'
4
-
5
- module Shells
6
- ##
7
- # Executes an SSH session with a host.
8
- #
9
- # The default setup of this class should work well with any bash-like shell.
10
- # In particular, the +exec_prompt+ method sets the "PS1" environment variable, which should set the prompt the shell
11
- # uses, and the +get_exit_code+ methods retrieves the value of the "$?" variable which should contain the exit code
12
- # from the last action. Because there is a possibility that your shell does not utilize those methods, the
13
- # +override_set_prompt+ and +override_get_exit_code+ options are available to change the behavior.
14
- #
15
- #
16
- # Valid options:
17
- # +host+::
18
- # The name or IP address of the host to connect to. Defaults to 'localhost'.
19
- # +port+::
20
- # The port on the host to connect to. Defaults to 22.
21
- # +user+::
22
- # The user to login with. This option is required.
23
- # +password+::
24
- # The password to login with.
25
- # If our public key is an authorized key on the host, the password is ignored for connection.
26
- # The #sudo_exec method for bash-like shells will also use this password for elevation.
27
- # +prompt+::
28
- # The prompt used to determine when processes finish execution.
29
- # Defaults to '~~#', but if that doesn't work for some reason because it is valid output from one or more
30
- # commands, you can change it to something else. It must be unique and cannot contain certain characters.
31
- # The characters you should avoid are !, $, \, /, ", and ' because no attempt is made to escape them and the
32
- # resulting prompt can very easily become something else entirely. If they are provided, they will be
33
- # replaced to protect the shell from getting stuck.
34
- # +shell+::
35
- # If set to :shell, then the default shell is executed. This is the default value.
36
- # If set to :none, then no shell is executed, but a PTY is still created.
37
- # If set to :no_pty, then no shell is executed and no PTY is created.
38
- # If set to anything else, it is assumed to be the executable path to the shell you want to run.
39
- # +quit+::
40
- # If set, this defines the command to execute when quitting the session.
41
- # The default is "exit" which will probably work most of the time.
42
- # +retrieve_exit_code+::
43
- # If set to a non-false value, then the default behavior will be to retrieve the exit code from the shell after
44
- # executing a command. If set to a false or nil value, the default behavior will be to ignore the exit code
45
- # from the shell. When retrieved, the exit code is stored in the +last_exit_code+ property.
46
- # This option can be overridden by providing an alternate value to the +exec+ method on a case-by-case basis.
47
- # +on_non_zero_exit_code+::
48
- # If set to :ignore (the default) then non-zero exit codes will not cause errors. You will still be able to check
49
- # the +last_exit_code+ property to determine if the command was successful.
50
- # If set to :raise then non-zero exit codes will cause a Shells::NonZeroExitCode to be raised when a command exits
51
- # with a non-zero return value.
52
- # This option only comes into play when +retrieve_exit_code+ is set to a non-false value.
53
- # This option can be overridden by providing an alternate value to the +exec+ method on a case-by-case basis.
54
- # +silence_timeout+::
55
- # When a command is executing, this is the maximum amount of time to wait for any feedback from the shell.
56
- # If set to 0 (or less) there is no timeout.
57
- # Unlike +command_timeout+ this value resets every time we receive feedback.
58
- # This option can be overridden by providing an alternate value to the +exec+ method on a case-by-case basis.
59
- # +command_timeout+::
60
- # When a command is executing, this is the maximum amount of time to wait for the command to finish.
61
- # If set to 0 (or less) there is no timeout.
62
- # Unlike +silence_timeout+ this value does not reset when we receive feedback.
63
- # This option can be overridden by providing an alternate value to the +exec+ method on a case-by-case basis.
64
- # +connect_timeout+::
65
- # This is the maximum amount of time to wait for the initial connection to the SSH shell.
66
- # +override_set_prompt+::
67
- # If provided, this must be set to either a command string that will set the prompt, or a Proc that accepts
68
- # the shell as an argument.
69
- # If set to a string, the string is sent to the shell and we wait up to two seconds for the prompt to appear.
70
- # If that fails, we resend the string and wait one more time before failing.
71
- # If set to a Proc, the Proc is called. If the Proc returns a false value, we fail. If the Proc returns
72
- # a non-false value, we consider it successful.
73
- # +override_get_exit_code+::
74
- # If provided, this must be set to either a command string that will retrieve the exit code, or a Proc that
75
- # accepts the shell as an argument.
76
- # If set to a string, the string is sent to the shell and the output is parsed as an integer and used as the exit
77
- # code.
78
- # If set to a Proc, the Proc is called and the return value of the proc is used as the exit code.
79
- #
80
- # Shells::SshSession.new(
81
- # host: '10.10.10.10',
82
- # user: 'somebody',
83
- # password: 'super-secret'
84
- # ) do |shell|
85
- # shell.exec('cd /usr/local/bin')
86
- # user_bin_files = shell.exec('ls -A1').split("\n")
87
- # @app_is_installed = user_bin_files.include?('my_app')
88
- # end
89
- #
90
- class SshSession < Shells::ShellBase
91
-
92
- include Shells::BashCommon
93
-
94
- ##
95
- # The error raised when we failed to request a PTY.
96
- class FailedToRequestPty < Shells::ShellError
97
-
98
- end
99
-
100
- ##
101
- # The error raised when we fail to start the shell on the PTY.
102
- class FailedToStartShell < Shells::ShellError
103
-
104
- end
105
-
106
-
107
- protected
108
-
109
- def validate_options #:nodoc:
110
- options[:host] ||= 'localhost'
111
- options[:port] ||= 22
112
- options[:shell] ||= :shell
113
- options[:quit] ||= 'exit'
114
- options[:connect_timeout] ||= 5
115
-
116
- raise InvalidOption, 'Missing host.' if options[:host].to_s.strip == ''
117
- raise InvalidOption, 'Missing user.' if options[:user].to_s.strip == ''
118
- end
119
-
120
- def exec_shell(&block) #:nodoc:
121
-
122
- ignore_io_error = false
123
- begin
124
-
125
- Net::SSH.start(
126
- options[:host],
127
- options[:user],
128
- password: options[:password],
129
- port: options[:port],
130
- non_interactive: true,
131
- timeout: options[:connect_timeout]
132
- ) do |ssh|
133
-
134
- # open the channel
135
- debug 'Opening channel...'
136
- @channel = ssh.open_channel do |ch|
137
- # start buffering the channel output.
138
- buffer_input
139
-
140
- if options[:shell] == :no_pty
141
- debug 'Executing session without PTY...'
142
-
143
- ssh_exec_session &block
144
- else
145
- # request a PTY
146
- debug 'Requesting PTY...'
147
- ch.request_pty do |ch_pty, success_pty|
148
- raise FailedToRequestPty unless success_pty
149
-
150
- if options[:shell] == :none
151
- debug 'Executing session without shell...'
152
-
153
- ssh_exec_session &block
154
- else
155
- # pick a method to start the shell with.
156
- meth = (options[:shell] == :shell) ? :send_channel_request : :exec
157
-
158
- # start the shell
159
- debug 'Starting shell...'
160
- ch_pty.send(meth, options[:shell].to_s) do |ch_sh, success_sh|
161
- raise FailedToStartShell unless success_sh
162
-
163
- # give the shell a chance to get ready.
164
- sleep 0.25
165
-
166
- debug 'Executing session in shell...'
167
- ssh_exec_session &block
168
- end
169
- end
170
- end
171
- end
172
- end
173
-
174
- debug 'Waiting for channel to close...'
175
- @channel.wait
176
- debug 'Channel has been closed.'
177
-
178
- end
179
- rescue IOError
180
- unless ignore_io_error
181
- raise
182
- end
183
- ensure
184
- @channel = nil
185
- end
186
-
187
- end
188
-
189
- def exec_prompt(&block) #:nodoc:
190
- cmd = options[:override_set_prompt] || "PS1=\"#{options[:prompt]}\""
191
- if cmd.respond_to?(:call)
192
- raise Shells::FailedToSetPrompt unless cmd.call(self)
193
- else
194
- # set the prompt, wait up to 2 seconds for a response, then try one more time.
195
- begin
196
- exec cmd, command_timeout: 2, retrieve_exit_code: false, command_is_echoed: false
197
- rescue Shells::CommandTimeout
198
- begin
199
- exec cmd, command_timeout: 2, retrieve_exit_code: false, command_is_echoed: false
200
- rescue Shells::CommandTimeout
201
- raise Shells::FailedToSetPrompt
202
- end
203
- end
204
- end
205
-
206
- # yield to the block
207
- block.call
208
- end
209
-
210
- def send_data(data) #:nodoc:
211
- @channel.send_data data
212
- debug "Sent: (#{data.size} bytes) #{(data.size > 32 ? (data[0..30] + '...') : data).inspect}"
213
- end
214
-
215
- def loop(&block) #:nodoc:
216
- @channel.connection.loop(&block)
217
- end
218
-
219
- def stdout_received(&block) #:nodoc:
220
- @channel.on_data do |_,data|
221
- debug "Received: (#{data.size} bytes) #{(data.size > 32 ? (data[0..30] + '...') : data).inspect}"
222
- block.call data
223
- end
224
- end
225
-
226
- def stderr_received(&block) #:nodoc:
227
- @channel.on_extended_data do |_, type, data|
228
- if type == 1
229
- debug "Received: (#{data.size} bytes) [E] #{(data.size > 32 ? (data[0..30] + '...') : data).inspect}"
230
- block.call data
231
- end
232
- end
233
- end
234
-
235
- private
236
-
237
- def ssh_exec_session(&block)
238
- begin
239
- block.call
240
- ensure
241
- # send the exit command.
242
- ignore_io_error = true
243
- debug 'Closing connection...'
244
- send_data options[:quit] + line_ending
245
- end
246
- end
247
-
248
- end
249
- end