shells 0.1.23 → 0.2.0

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.
@@ -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