Skip to content

Instantly share code, notes, and snippets.

@baude
Created May 23, 2018 21:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baude/9353c6cf83643863efc32d9460ef5cfb to your computer and use it in GitHub Desktop.
Save baude/9353c6cf83643863efc32d9460ef5cfb to your computer and use it in GitHub Desktop.
import socket, argparse
import select, sys, os, tty, atexit, termios, signal, shutil
import fcntl, termios, struct
# Create the socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
# parsing args for using this as a cli .... dwalsh hates the idea so you can
# nuke this when folding into the mix
parser = argparse.ArgumentParser()
parser.add_argument('container', help='container name or id')
args = parser.parse_args()
# Setup the dir where the sockets live ... you have to use this path
# even though there are other paths, you gotta use this one or the path
# will be too long and wont work but also wont tell you why.
sockets_dir = os.path.join("/var/run/libpod/socket/", args.container)
# This is the UDS where all the IO goes
socket_path = os.path.join(sockets_dir, "attach")
# This is the control socket where resizing events are sent to conmon
control_socket_path = os.path.join(sockets_dir, "ctl")
def reset_fd(my_fd, old_settings):
"""
resets an fd to the old settings (whatever it was before we took control). this should be done
on any exit
:param my_fd:
:param old_settings:
:return:
"""
termios.tcsetattr(my_fd, termios.TCSADRAIN, old_settings)
os.close(my_fd)
def error_print(*args, **kwargs):
"""
dumps a proper error message to stdout for an error. only used for socket lose at this time.
:param args:
:param kwargs:
:return: none
"""
print(*args, file=sys.stderr, **kwargs)
def terminal_size():
"""
determines the height and width of a window when it is resized.
:return: width and height of the terminal window
"""
h, w, hp, wp = struct.unpack('HHHH',
fcntl.ioctl(1, termios.TIOCGWINSZ,
struct.pack('HHHH', 0, 0, 0, 0)))
return w, h
def resize_handler(signum, frame):
"""
sends the resizing of the window to the conmon fifo socket. the format of the message needs to be:
"%d %d %d\n", 1, size.Height, size.Width)
the parameters to this def are not used but are required. leave alone.
This def should be called when a signal is caught for the window resizing.
:param signum:
:param frame:
:return: nothing
"""
control_socket = open(control_socket_path, 'w')
w, h = terminal_size()
control_socket.write("{} {} {}\n".format(1, h, w))
control_socket.close()
# catch any resizing events and send the resize info
# to the control fifo "socket"
signal.signal(signal.SIGWINCH, resize_handler)
# connect
sock.connect(socket_path)
# prime stdin
stdin = os.open("/dev/stdin", os.O_RDWR)
# save off the old settings for stdin
old_settings = termios.tcgetattr(stdin)
# if we quit, clean up and reset the console or bad things happen
# that involve kittens and puppies
atexit.register(reset_fd, stdin, old_settings)
# always send a resize on connection because if someone else used it before, it will be
# stuck on their size.
resize_handler(0,0)
# set stdin to raw
tty.setraw(stdin)
# iterator for where we can get stuff ... i.e. the container and keyboard
inputs = [sock, stdin]
# we dont want to block on the outputs, so we none
outputs = []
while inputs:
try:
# really the magic is here, iterate on input sources
readable, writable, exceptional = select.select(inputs, outputs, inputs)
for r in readable:
# This iterator is the UDS container socket
if isinstance(r, socket.socket):
# should closely resemble conmons read size
# scoop some data
data = r.recv(8192)
# knock off the first byte ... it tells us where it came from. we dont care really
foo = data[1:]
# write this to stdout
os.write(1, foo)
# This is the keyboard basically
if isinstance(r, int):
# should closely resemble conmons read size
# scoop some data
data = os.read(stdin, 8192)
# if control-], bail
if 29 in data: # Bail on ctrl-[
sys.exit(0)
# send the input back on the UDS socket
sock.send(data)
except BrokenPipeError:
error_print("socket quit on container end")
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment