Yesterday we were discussing scoped persistent connections to emulate a terminal session. We said one way to do it would be to use a real session and overlay the emulated session on top of it. This translates to the user seeing an emulated session and not knowing the difference that there is no interpreter associated with it. The command are merely piped forward and backwards through the real shell console.
We pointed out that there are two independent objects that have a scope and maintain a timeline. One is the session concerning the real shell console which is instantiated and held so that user commands on the emulated window flow here and the results flow back. Typically this real shell console's lifetime is maintained across all the commands issued by the user. While each command may be stateless, it is the real console that maintains state between the commands so that the results of the previous commands are available to the user. Consequently the API to redirect the commands from the user to the console is stateless but the console is stateful.
This API middle layer in fact looks very much like any programming's shell command invocation. For example, in python we do:
We are merely pointing out that the Popen is separated from the communicate. The Popen is done in a setup phase. The communicate happens for each user command as and when it is invoked and the teardown happens via the cancelation of the process. They are not all done together otherwise cmd_execute becomes stateless and we want this to be stateful. Furthermore the setup and teardown are called once for the duration of the real console. And we pointed out this is the case with any networking protocol. Consequently the considerations for securing and functionality of the communication are the same as with networking protocols.
The second connection is the user session which dictates how the commands are tied together. This is maintained with a tag such as a request.session.session_key. Over all the request flowing in to the real console, we can tell apart those that belong to a user by this key. When the commands and outputs are captured for the same session key, we have the content for the emulation window.
The emulation happens with an emulator like so:
The display of the emulated console is merely a repetition of single line <div> elements that all look the same with a black window and beat the text associated with the corresponding line in the real console.
We pointed out that there are two independent objects that have a scope and maintain a timeline. One is the session concerning the real shell console which is instantiated and held so that user commands on the emulated window flow here and the results flow back. Typically this real shell console's lifetime is maintained across all the commands issued by the user. While each command may be stateless, it is the real console that maintains state between the commands so that the results of the previous commands are available to the user. Consequently the API to redirect the commands from the user to the console is stateless but the console is stateful.
This API middle layer in fact looks very much like any programming's shell command invocation. For example, in python we do:
class ScopedRealConsole(): def __init__(self, ): self.process = None self.kill_proc = None self.timer = None def setup(self, preamble): import shlex, subprocess from threading import Timer from subprocess import Popen, PIPE self.process = subprocess.Popen(preamble, stdin = subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) self.kill_proc = lambda p: p.kill() self.timer = Timer(10, kill_proc, [process]) def teardown(self): if self.process != None: self.process.kill() def execute(self, cmds): output = '' try: timer.start() for i in range (1,len(cmds)): self.process.stdin.write(str.encode(cmds[i])) output, stderr = self.process.communicate() output = output.decode('utf-8') finally: timer.cancel() return output
The second connection is the user session which dictates how the commands are tied together. This is maintained with a tag such as a request.session.session_key. Over all the request flowing in to the real console, we can tell apart those that belong to a user by this key. When the commands and outputs are captured for the same session key, we have the content for the emulation window.
The emulation happens with an emulator like so:
<div id="term_demo"></div> <p>Javascript code:</p> <pre class="javascript">jQuery(function($, undefined) { $('#term_demo').terminal(function(command, term) { if (command !== '') { try { var result = cmd_execute(command); if (result !== undefined) { term.echo(new String(result)); } } catch(e) { term.error(new String(e)); } } else { term.echo(''); } }, { greetings: 'Emulation of a shell in an insolate Linux Container', name: 'bash_demo', height: 200, prompt: 'bash> ' }); });</pre>and the user session dictates how long the page for the emulator is shown. Each page is unique to the user session and the destination on which the commands are targeted. The destination is analogous to the well-known screen command of the terminal and comes with an identifier in addition to the IP address of the machine on which it is targeted.
The display of the emulated console is merely a repetition of single line <div> elements that all look the same with a black window and beat the text associated with the corresponding line in the real console.
No comments:
Post a Comment