[weekend] Real-Time BASH To HTTP Viewer

So for some reason, I decided to make python call up a bash shell while outputting everything from the terminal to a web page. I don’t think this is supposed to even work but with FF4 it seems to. Basically the python script starts by setting stdin to raw (canon) i/o mode. Then it forks a child process (bash) in which it can write to it’s stdin and read from it’s stdout/stderror. It then fires up a simple TCP socket HTTP server which sends any viewers the output of the shell’s stdout. It uses basic alarm interrupts to prevent blocking calls and basically acts as a service-in-the-middle. In English, this thing lets web viewers monitor your command line input/output. I don’t know why you would even want or need this but I guess it’s a cool hack/proof of concept.

#!/usr/bin/python

import os
import re
import signal
import socket
import sys
import time

import tty
import termios

def cleanstr(inptstri):
	backspce = (chr(8) + chr(27) + "[K")
	outpstri = inptstri
	
	outpstri = outpstri.replace(backspce, "^H")
	outpstri = outpstri.replace("&", "&")
	outpstri = outpstri.replace("<", "&lt;")
	outpstri = outpstri.replace(">", "&gt;")
	outpstri = outpstri.replace(" ", "&nbsp;")
	outpstri = outpstri.replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;")
	outpstri = outpstri.replace("\r", "")
	outpstri = outpstri.replace("\n", "<br /> \n")
	
	fnalstri = ""
	
	for c in outpstri:
		d = ord(c)
		if ((d != 10) and (d < 32)):
			fnalstri += ("[%d]" % (d))
		else:
			fnalstri += c
	
	return fnalstri

# define a dummy alarm handler

def alarmhan(signanum, framenum):
	pass

signal.signal(signal.SIGALRM, alarmhan)

# spawn a new child shell

(shelcpid, shelcdes) = os.forkpty()

if (shelcpid == 0):
	os.execve("/bin/bash", [], os.environ)
	sys.exit(0)

# init some socket variables for our server

hostname = ""
portnumb = 8080
sockobjc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sockobjc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sockobjc.bind((hostname, portnumb))
sockobjc.listen(5)
sockobjc.setblocking(0)

# set stdin i/o to raw (canon) mode

stdidesc = sys.stdin.fileno()
stdiolds = termios.tcgetattr(stdidesc)
tty.setraw(stdidesc)

# become a man-in-the-middle : user <-> us <-> shell -> us -> viewer

timeouta = 0.05
socklist = []

while (1):
	signal.setitimer(signal.ITIMER_REAL, timeouta)
	try:
		c = sys.stdin.read(1)
		if (ord(c) == 4):
			break
		if (ord(c) == 13):
			c = chr(10)
		os.write(shelcdes, c)
	except:
		pass
	signal.alarm(0)
	
	try:
		(clieconn, clieaddr) = sockobjc.accept()
		clieconn.send("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
		clieconn.send("<script>function stob() { window.scrollTo(0, document.body.scrollHeight); } setInterval('stob();', 250);</script> \n")
		for x in range(0, 64):
			clieconn.send(" &nbsp; <br /> \n ")
		socklist.append(clieconn)
	except:
		pass
	
	signal.setitimer(signal.ITIMER_REAL, timeouta)
	try:
		r = os.read(shelcdes, 1024)
		sys.stdout.write(r)
		sys.stdout.flush()
		l = []
		s = cleanstr(r)
		for sockitem in socklist:
			try:
				sockitem.send(s)
			except:
				l.append(sockitem)
		for i in l:
			socklist.remove(i)
	except:
		pass
	signal.alarm(0)

termios.tcsetattr(stdidesc, termios.TCSADRAIN, stdiolds)
print("")

Leave a comment