Skip to content

Commit

Permalink
Deprecate real-readline option
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanusz-r7 committed Nov 18, 2024
1 parent acc9940 commit 5cbd236
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 84 deletions.
1 change: 0 additions & 1 deletion lib/metasploit/framework/command/console.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ def driver_options
driver_options['ModulePath'] = options.modules.path
driver_options['Plugins'] = options.console.plugins
driver_options['Readline'] = options.console.readline
driver_options['RealReadline'] = options.console.real_readline
driver_options['Resource'] = options.console.resources
driver_options['XCommands'] = options.console.commands

Expand Down
8 changes: 6 additions & 2 deletions lib/metasploit/framework/parsed_options/console.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ def options
options.console.plugins = []
options.console.quiet = false
options.console.readline = true
options.console.real_readline = false
options.console.resources = []
options.console.subcommand = :run
}
Expand Down Expand Up @@ -54,7 +53,12 @@ def option_parser
end

option_parser.on('-L', '--real-readline', 'Use the system Readline library instead of RbReadline') do
options.console.real_readline = true
message = "The RealReadline option has been marked as deprecated, and is currently a noop.\n"
message << "Metasploit Framework now uses RbReadline exclusively as the input handling library, with the option to use Reline.\n"
message << "Reline is planned to be the only UI library used in the future.\n"
message << "If you require this functionality, please use the following link to tell us:\n"
message << ' https://github.com/rapid7/metasploit-framework/issues/19399'
warn message
end

option_parser.on('-o', '--output FILE', 'Output to the specified file') do |file|
Expand Down
1 change: 0 additions & 1 deletion lib/metasploit/framework/parsed_options/remote_db.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ def options
options.console.local_output = nil
options.console.plugins = []
options.console.quiet = false
options.console.real_readline = false
options.console.resources = []
options.console.subcommand = :run
}
Expand Down
117 changes: 37 additions & 80 deletions lib/msf/ui/console/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ class Driver < Msf::Ui::Driver
# @option opts [Boolean] 'AllowCommandPassthru' (true) Whether to allow
# unrecognized commands to be executed by the system shell
# @option opts [Boolean] 'Readline' (true) Whether to use the readline or not
# @option opts [Boolean] 'RealReadline' (false) Whether to use the system's
# readline library instead of RBReadline
# @option opts [String] 'HistFile' (Msf::Config.history_file) Path to a file
# where we can store command history
# @option opts [Array<String>] 'Resources' ([]) A list of resource files to
Expand All @@ -64,7 +62,7 @@ class Driver < Msf::Ui::Driver
# @option opts [Boolean] 'SkipDatabaseInit' (false) Whether to skip
# connecting to the database and running migrations
def initialize(prompt = DefaultPrompt, prompt_char = DefaultPromptChar, opts = {})
choose_readline(opts)
setup_readline if opts['Readline']

histfile = opts['HistFile'] || Msf::Config.history_file

Expand Down Expand Up @@ -132,14 +130,6 @@ def initialize(prompt = DefaultPrompt, prompt_char = DefaultPromptChar, opts = {
# stack
enstack_dispatcher(CommandDispatcher::Core)

# Report readline error if there was one..
if !@rl_err.nil?
print_error("***")
print_error("* Unable to load readline: #{@rl_err}")
print_error("* Falling back to RbReadLine")
print_error("***")
end

# Load the other "core" command dispatchers
CommandDispatchers.each do |dispatcher_class|
dispatcher = enstack_dispatcher(dispatcher_class)
Expand Down Expand Up @@ -706,77 +696,44 @@ def handle_session_tlv_logging(val)
# Require the appropriate readline library based on the user's preference.
#
# @return [void]
def choose_readline(opts)
# Choose a readline library before calling the parent
@rl_err = nil
if opts['RealReadline']
# Remove the gem version from load path to be sure we're getting the
# stdlib readline.
gem_dir = Gem::Specification.find_all_by_name('rb-readline').first.gem_dir
rb_readline_path = File.join(gem_dir, "lib")
index = $LOAD_PATH.index(rb_readline_path)
# Bundler guarantees that the gem will be there, so it should be safe to
# assume we found it in the load path, but check to be on the safe side.
if index
$LOAD_PATH.delete_at(index)
end
end
def setup_readline
# Only Windows requires a monkey-patched RbReadline
return unless Rex::Compat.is_windows

if defined?(::RbReadline) && !defined?(RbReadline.refresh_console_handle)
::RbReadline.instance_eval do
class << self
alias_method :old_rl_move_cursor_relative, :_rl_move_cursor_relative
alias_method :old_rl_get_screen_size, :_rl_get_screen_size
alias_method :old_space_to_eol, :space_to_eol
alias_method :old_insert_some_chars, :insert_some_chars
end

begin
require 'readline'

# Only Windows requires a monkey-patched RbReadline
return unless Rex::Compat.is_windows

if defined?(::RbReadline) && !defined?(RbReadline.refresh_console_handle)
::RbReadline.instance_eval do
class << self
alias_method :old_rl_move_cursor_relative, :_rl_move_cursor_relative
alias_method :old_rl_get_screen_size, :_rl_get_screen_size
alias_method :old_space_to_eol, :space_to_eol
alias_method :old_insert_some_chars, :insert_some_chars
end

def self.refresh_console_handle
# hConsoleHandle gets set only when RbReadline detects it is running on Windows.
# Therefore, we don't need to check Rex::Compat.is_windows, we can simply check if hConsoleHandle is nil or not.
@hConsoleHandle = @GetStdHandle.Call(::Readline::STD_OUTPUT_HANDLE) if @hConsoleHandle
end

def self._rl_move_cursor_relative(*args)
refresh_console_handle
old_rl_move_cursor_relative(*args)
end

def self._rl_get_screen_size(*args)
refresh_console_handle
old_rl_get_screen_size(*args)
end

def self.space_to_eol(*args)
refresh_console_handle
old_space_to_eol(*args)
end

def self.insert_some_chars(*args)
refresh_console_handle
old_insert_some_chars(*args)
end
def self.refresh_console_handle
# hConsoleHandle gets set only when RbReadline detects it is running on Windows.
# Therefore, we don't need to check Rex::Compat.is_windows, we can simply check if hConsoleHandle is nil or not.
@hConsoleHandle = @GetStdHandle.Call(::Readline::STD_OUTPUT_HANDLE) if @hConsoleHandle
end

def self._rl_move_cursor_relative(*args)
refresh_console_handle
old_rl_move_cursor_relative(*args)
end

def self._rl_get_screen_size(*args)
refresh_console_handle
old_rl_get_screen_size(*args)
end

def self.space_to_eol(*args)
refresh_console_handle
old_space_to_eol(*args)
end

def self.insert_some_chars(*args)
refresh_console_handle
old_insert_some_chars(*args)
end
end
rescue ::LoadError => e
if @rl_err.nil? && index
# Then this is the first time the require failed and we have an index
# for the gem version as a fallback.
@rl_err = e
# Put the gem back and see if that works
$LOAD_PATH.insert(index, rb_readline_path)
index = rb_readline_path = nil
retry
else
# Either we didn't have the gem to fall back on, or we failed twice.
# Nothing more we can do here.
raise e
end
end
end
Expand Down

0 comments on commit 5cbd236

Please sign in to comment.