chiark / gitweb /
fishdescriptor: wip utility, before rename various variables
[chiark-utils.git] / fishdescriptor / fishdescriptor
index 7150df2b31fc587f711b3826a7a90a296558e0de..bdddff2a12e380a97971669a76b3603c10b61f89 100755 (executable)
@@ -2,6 +2,157 @@
 
 import sys
 import fishdescriptor.fish
+import optparse
+import re
+
+donor = None
+
+usage =
+'''
+fishdescriptor [-p|--pid] <pid> <action>... [-p|--pid <pid> <action>...]
+
+<action>s
+  [<here-0fd>=]<there-fd>
+          fish the openfile referenced by descriptor <there-fd> in
+          (the most recent) <pid> and keep a descriptor onto it;
+          and, optionally, give it the number <here-fd> for exec
+  exec <program> [<arg>...]
+          execute a process with each specified <here>
+          as an actual fd
+  sockinfo
+          calls getsockname/getpeername on the most recent
+          <there-fd>
+
+  -p|-pid <pid>
+          now attach to <pid>, 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 permute_fds_for_exec():
+    actual_map = { info[0]: intent for intent, info in fdmap.items }
+    for intent, (actual, tdonor, intarget) in fdmap.items():
+        if intent is not None:
+            in_way = actual_map.get(intent)
+            if in_way is not None:
+                moved = os.dup(intent)
+                actual_map[moved] = 
+
+def implement_exec(argl):
+    if donor is not None: donor.detach()
+    
+
+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 = [int(x) for x in sys.argv[2:]]