From 0870419ad2505efb827135e646c6248aab59511d Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 23 Oct 2017 17:38:56 +0100 Subject: [PATCH] fishdescriptor: wip utility Signed-off-by: Ian Jackson --- fishdescriptor/fishdescriptor | 139 +++++++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 29 deletions(-) diff --git a/fishdescriptor/fishdescriptor b/fishdescriptor/fishdescriptor index b8c732a..074221c 100755 --- a/fishdescriptor/fishdescriptor +++ b/fishdescriptor/fishdescriptor @@ -2,21 +2,22 @@ import sys import fishdescriptor.fish +import optparse import re donor = None usage = ''' -fishdescriptor ... [-p|--pid ...] +fishdescriptor [-p|--pid] ... [-p|--pid ...] s - [=] + [=] fish the openfile referenced by descriptor in (the most recent) and keep a descriptor onto it; - and, optionally, give it the number for exec + and, optionally, give it the number for exec exec [...] - execute a process with each specified numeric + execute a process with each specified as an actual fd sockinfo calls getsockname/getpeername on the most recent @@ -26,42 +27,122 @@ fishdescriptor ... [-p|--pid ...] now attach to , detaching from previous pid ''' -def set_donor(pid_arg): - pid = int(pid_arg) +fdmap = { } +# fdmap[here] = (actual_fd_number_here, Donor, target_fd) + +last_here = None + +def implement_pending(): + got_list = donor.fish([x[1] for x in pending]) + assert(len(got_list) = len(pending)) + for (here, there), got in zip(pending, got_list) + overwriting = fdmap.get(here) + if overwriting is not None: os.close(overwriting) + fdmap[here] = (got, Donor, there) + last_here = here + +def implmement_sockinfo(here): + (fd, tdonor, there) = fdmap[here] + # socket.fromfd requires the AF. But of course we don't know the AF. + # There isn't a sane way to get it in Python: + # https://utcc.utoronto.ca/~cks/space/blog/python/SocketFromFdMistake + # Rejected options: + # https://github.com/tiran/socketfromfd + # adds a dependency, not portable due to reliance on SO_DOMAIN + # call getsockname using ctypes + # no sane way to discover how to unpack sa_family_t + perl_script = ''' + use strict; + use Socket; + use POSIX; + my $sa = getsockname STDIN; + exit 0 if !defined $sa and $!==ENOTSOCK; + my $family = sockaddr_family $sa; + print $family, "\n" or die $!; + ''' + famp = subprocess.Popen( + stdin = fd, + stdout = subprocess.PIPE, + args = ['perl','-we',perl_script] + ) + (output, dummy) = famp.communicate() + family = int(output) + + sock = socket.fromfd(fd, family, 0) + + print("[%s] %d sockinfo" % (tdonor.pid, there), end='') + for f in (lambda: socket.AddressFamily(family).name, + lambda: repr(sock.getsockname()), + lambda: repr(sock.getpeername())): + try: info = f() + except Exception as e: info = repr(e) + print("\t", info, sep='', end='') + print("") + + sock.close() + +def implement_exec(argl): + pass + +def set_donor(pid): + global donor if donor is not None: donor.detach() donor = fishdescriptor.fish.Donor(pid) +def ocb_set_donor(option, opt, value, parser): + set_donor(value) + +ov = optparse.Values() + def process_args(): def arg_matches(regexp): nonlocal m m = re.search(regexp, arg) return m - arg_ix = 0 - def next_arg(): - sys.argv[arg_ix] = + op = optparse.OptionParser(usage=usage) - while arg_ix < len(sys.argv): - arg = next_arg() - if arg_matches(r'^-p(\d+)') or - arg_matches(r'^--pid=(\d+)$') - set_donor(m.groups(1)[0]) - - pid = -(?:-p|^--pid(?:=|$))(\d*)$'): - - pid = int(m.groups(1)[0]) - elif arg_matches(r'^(?:-p|^--pid=)(\d+)$'): - - if expecting_pid: - set_donor(arg) - el - elif arg_matches(r'\d+ - - parser = OptionParser(usage=usage, ) + op.disable_interspersed_args() + op.add_option('-p','--pid', type='int', action='callback', + callback='ocb_set_donor') + + args = sys.argv + pending = [] + + while True: + (ov, args) = op.parse_args(args=args, values=ov) + if not len(args): break - parser.add_option("-p", "--pid", type='int', callback=set_donor) - parser.add_option( + arg = args.pop(0) + + if donor is not None: + set_donor(int(arg)) + elif arg_matches(r'^(?:(\d+)=)?(\d+)?$'): + (here, there) = m.groups() + here = None if here is None else int(here) + there = int(there) + pending.append = (here,there) + elif arg = 'exec': + if not len(args): + op.error("exec needs command to run") + implement_pending() + implement_exec(args) + elif arg = 'sockinfo': + if last_here is None: + op.error('sockinfo needs a prior fd spec') + implement_pending() + implement_sockinfo(last_here) + else: + op.error("unknown argument/option `%s'" % arg) + + implement_pending() + + + there = int(m.group[1]) + here = None if m.group + + ,here) = map(int, m.groups()) + pid = int(sys.argv[1]) fds = [int(x) for x in sys.argv[2:]] -- 2.30.2