Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/ui/interactive.rb
Views: 11780
# -*- coding: binary -*-1module Rex2module Ui34###5#6# This class implements the stubs that are needed to provide an interactive7# user interface that is backed against something arbitrary.8#9###10module Interactive1112#13# Interactive sessions by default may interact with the local user input14# and output.15#16include Rex::Ui::Subscriber1718#19# Starts interacting with the session at the most raw level, simply20# forwarding input from user_input to rstream and forwarding input from21# rstream to user_output.22#23def interact(user_input, user_output)2425# Detach from any existing console26if self.interacting27detach()28end2930init_ui(user_input, user_output)3132self.interacting = true33self.completed = false3435eof = false3637# Start the readline stdin monitor38# XXX disabled39# user_input.readline_start() if user_input.supports_readline4041# Handle suspend notifications42handle_suspend4344handle_usr14546handle_winch4748# As long as we're interacting...49while (self.interacting == true)5051begin52_interact5354rescue Interrupt55# If we get an interrupt exception, ask the user if they want to56# abort the interaction. If they do, then we return out of57# the interact function and call it a day.58eof = true if (_interrupt)5960rescue EOFError, Errno::ECONNRESET, IOError61# If we reach EOF or the connection is reset...62eof = true6364end6566break if eof67end6869begin7071# Restore the suspend handler72restore_suspend7374restore_winch7576# If we've hit eof, call the interact complete handler77_interact_complete if (eof == true)7879# Shutdown the readline thread80# XXX disabled81# user_input.readline_stop() if user_input.supports_readline8283# Detach from the input/output handles84reset_ui()8586ensure87# Mark this as completed88self.completed = true89end9091# if another session was requested, store it92next_session = self.next_session93# clear the value from the object94self.next_session = nil9596# return this session id97return next_session98end99100#101# Stops the current interaction102#103def detach104if (self.interacting)105self.interacting = false106while(not self.completed)107::IO.select(nil, nil, nil, 0.25)108end109end110end111112#113# Whether or not the session is currently being interacted with114#115attr_accessor :interacting116117#118# If another session needs interaction, this is where it goes119#120attr_accessor :next_session121122#123# Whether or not the session has completed interaction124#125attr_accessor :completed126127attr_accessor :on_print_proc128attr_accessor :on_command_proc129130#131# A function to be run when running a session command hits an error132#133# @return [Proc,nil] A function to be run when running a session command hits an error134attr_accessor :on_run_command_error_proc135136protected137138#139# The original suspend proc.140#141attr_accessor :orig_suspend142attr_accessor :orig_usr1143attr_accessor :orig_winch144145#146# Stub method that is meant to handler interaction147#148def _interact149end150151#152# Called when an interrupt is sent.153#154def _interrupt155true156end157158#159# Called when a suspend is sent.160#161def _suspend162false163end164165#166# Called when interaction has completed and one of the sides has closed.167#168def _interact_complete169true170end171172#173# Read from remote and write to local.174#175def _stream_read_remote_write_local(stream)176data = stream.get177178self.on_print_proc.call(data) if self.on_print_proc179user_output.print(data)180end181182#183# Read from local and write to remote.184#185def _stream_read_local_write_remote(stream)186data = user_input.gets187188self.on_command_proc.call(data) if self.on_command_proc189stream.put(data)190end191192#193# The local file descriptor handle.194#195def _local_fd196user_input.fd197end198199#200# The remote file descriptor handle.201#202def _remote_fd(stream)203stream.fd204end205206#207# Interacts with two streaming connections, reading data from one and208# writing it to the other. Both are expected to implement Rex::IO::Stream.209#210def interact_stream(stream)211while self.interacting && _remote_fd(stream)212213# Select input and rstream214sd = Rex::ThreadSafe.select([ _local_fd, _remote_fd(stream) ], nil, nil, 0.25)215216# Cycle through the items that have data217# From the stream? Write to user_output.218sd[0].each { |s|219if (s == _remote_fd(stream))220_stream_read_remote_write_local(stream)221# From user_input? Write to stream.222elsif (s == _local_fd)223_stream_read_local_write_remote(stream)224end225} if (sd)226227Thread.pass228end229end230231232#233# Installs a signal handler to monitor suspend signal notifications.234#235def handle_suspend236if orig_suspend.nil?237begin238self.orig_suspend = Signal.trap("TSTP") do239Thread.new { _suspend }.join240end241rescue242end243end244end245246247#248# Restores the previously installed signal handler for suspend249# notifications.250#251def restore_suspend252begin253if orig_suspend254Signal.trap("TSTP", orig_suspend)255else256Signal.trap("TSTP", "DEFAULT")257end258self.orig_suspend = nil259rescue260end261end262263def handle_usr1264if orig_usr1.nil?265begin266self.orig_usr1 = Signal.trap("USR1") do267Thread.new { _usr1 }.join268end269rescue270end271end272end273274def handle_winch275if orig_winch.nil?276begin277self.orig_winch = Signal.trap("WINCH") do278Thread.new { _winch }.join279end280rescue281end282end283end284285def restore_winch286begin287if orig_winch288Signal.trap("WINCH", orig_winch)289else290Signal.trap("WINCH", "DEFAULT")291end292self.orig_winch = nil293rescue294end295end296297def _winch298end299300def restore_usr1301begin302if orig_usr1303Signal.trap("USR1", orig_usr1)304else305Signal.trap("USR1", "DEFAULT")306end307self.orig_usr1 = nil308rescue309end310end311312#313# Prompt the user for input if possible.314# XXX: This is not thread-safe on Windows315#316def prompt(query)317if (user_output and user_input)318user_output.print("\n" + query)319user_input.sysread(2)320end321end322323#324# Check the return value of a yes/no prompt325#326def prompt_yesno(query)327(prompt(query + " [y/N] ") =~ /^y/i) ? true : false328end329330end331332end333end334335336337