#!/usr/bin/env python # ultimately, this should provide an interactive cli for navigating confluent # tree and doing console with a socket. ]0; can be used to # present info such as whether it is in a console or other mode and, if in # console, how many other connections are live and looking # this means 'wcons' simply needs to make a terminal run and we'll take care of # the title while providing more info # this also means the socket interface needs to have ways to convey more # interesting pieces of data (like concurrent connection count) # socket will probably switch to a TLV scheme: # 32 bit TL, 8 bits of type code and 24 bit size # type codes: # 0: string data # 1: json data # 24 bit size allows the peer to avoid having to do any particular parsing to # understand message boundaries (which is a significant burden on the xCAT # protocol) # When in a console client mode, will recognize two escape sequences by # default: # Ctrl-E, c, ?: mimick conserver behavior # ctrl-]: go to interactive prompt (telnet escape, but not telnet prompt) # esc-( would interfere with normal esc use too much # ~ I will not use for now... import fcntl import optparse import os import select import socket import ssl import sys import termios import tty SO_PASSCRED = 16 conserversequence = '\x05c' # ctrl-e, c oldtcattr = termios.tcgetattr(sys.stdin.fileno()) server = None def parseservervalue(serverstring): if serverstring.find(']:') != -1: server, port = serverstring[1:].split(']:') elif serverstring[0] == '[': server = serverstring[1:-1] port = 4001 elif -1 != opts.server.find(':'): server, port = opts.server.split(":") else: server = serverstring port = 4001 return (server, port) def exit(code=0): termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr) server.shutdown(socket.SHUT_RDWR) server.close() sys.exit(code) def conserver_command(fh, command): while not command: ready, _, _ = select.select((fh,), (), (), 1) if ready: command += fh.read() if command[0] == '.': print("disconnect]\r") exit() elif command[0] == '?': print("help]\r") print(". disconnect\r") print(" abort command\r") elif command[0] == '\x0d': print("ignored]\r") else: #not a command at all.. print("unknown -- use '?']\r") def check_escape_seq(input, fh): while conserversequence.startswith(input): if input.startswith(conserversequence): # We have full sequence sys.stdout.write("[") sys.stdout.flush() return conserver_command(fh, input[len(conserversequence):]) ready, _, _ = select.select((fh,), (), (), 3) if not ready: # 3 seconds of no typing break input += fh.read() return input def connect_unix_server(sockpath): server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, SO_PASSCRED, 1) server.connect(sockpath) return server def connect_tls_server(serverstring): host, port = parseservervalue(serverstring) for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM): af, socktype, proto, cononname, sa = res try: server = socket.socket(af, socktype, proto) except: server = None continue try: server.settimeout(5) server.connect(sa) except: server.close() server = None continue break if server is None: sys.stderr.write("Failed to connect to %s\n" % serverstring) sys.exit(1) secserver = ssl.wrap_socket(server) return secserver parser = optparse.OptionParser() parser.add_option("-s", "--server", dest="server", help="TLS server to connect to", metavar="SERVER:PORT") parser.add_option("-u", "--unixsocket", dest="unixsock", help="TLS server to connect to", metavar="UNIXDOMAINSOCKET") opts, args = parser.parse_args() if opts.server: # going over a TLS network server = connect_tls_server(opts.server) elif opts.unixsock: server = connect_unix_server(opts.unixsock) #Next stop, reading and writing from whichever of stdin and server goes first. #see pyghmi code for solconnect.py tty.setraw(sys.stdin.fileno()) fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) # clear on start can help with readable of TUI, but it # can be annoying, so for now don't do it. # sys.stdout.write('\x1b[H\x1b[J') # sys.stdout.flush() doexit = False while not doexit: rdylist, _, _ = select.select((sys.stdin, server), (), (), 60) for fh in rdylist: if fh == server: #fh.read() data = fh.recv(16384) if data: sys.stdout.write(data) sys.stdout.flush() else: doexit = True sys.stdout.write("\r\n[remote disconnected]\r\n") break else: input = fh.read() input = check_escape_seq(input, fh) if input: server.sendall(input) exit()