X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-utils.git;a=blobdiff_plain;f=fishdescriptor%2Ffishdescriptor;h=074221c367f312b175858065719c70ea01fd1f22;hp=4708401f438ae91a9d362af36a48f68b5c64fce8;hb=0870419ad2505efb827135e646c6248aab59511d;hpb=d8d60f17dad9d2e9a01729d9f21b0a99f1aeb4ce diff --git a/fishdescriptor/fishdescriptor b/fishdescriptor/fishdescriptor index 4708401..074221c 100755 --- a/fishdescriptor/fishdescriptor +++ b/fishdescriptor/fishdescriptor @@ -2,9 +2,150 @@ import sys import fishdescriptor.fish +import optparse +import re + +donor = None + +usage = +''' +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 + exec [...] + execute a process with each specified + as an actual fd + sockinfo + calls getsockname/getpeername on the most recent + + + -p|-pid + now attach to , detaching from previous pid +''' + +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 + + op = optparse.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 + + 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 = map(int, sys.argv[2:]) +fds = [int(x) for x in sys.argv[2:]] d = fishdescriptor.fish.Donor(pid) r = d.fish(fds)