4 import fishdescriptor.fish
12 fishdescriptor [-p|--pid] <pid> <action>... [-p|--pid <pid> <action>...]
15 [<here-0fd>=]<there-fd>
16 fish the openfile referenced by descriptor <there-fd> in
17 (the most recent) <pid> and keep a descriptor onto it;
18 and, optionally, give it the number <here-fd> for exec
19 exec <program> [<arg>...]
20 execute a process with each specified <here>
23 calls getsockname/getpeername on the most recent
27 now attach to <pid>, detaching from previous pid
31 # fdmap[here] = (actual_fd_number_here, Donor, target_fd)
35 def implement_pending():
36 got_list = donor.fish([x[1] for x in pending])
37 assert(len(got_list) = len(pending))
38 for (here, there), got in zip(pending, got_list)
39 overwriting = fdmap.get(here)
40 if overwriting is not None: os.close(overwriting)
41 fdmap[here] = (got, Donor, there)
44 def implmement_sockinfo(here):
45 (fd, tdonor, there) = fdmap[here]
46 # socket.fromfd requires the AF. But of course we don't know the AF.
47 # There isn't a sane way to get it in Python:
48 # https://utcc.utoronto.ca/~cks/space/blog/python/SocketFromFdMistake
50 # https://github.com/tiran/socketfromfd
51 # adds a dependency, not portable due to reliance on SO_DOMAIN
52 # call getsockname using ctypes
53 # no sane way to discover how to unpack sa_family_t
58 my $sa = getsockname STDIN;
59 exit 0 if !defined $sa and $!==ENOTSOCK;
60 my $family = sockaddr_family $sa;
61 print $family, "\n" or die $!;
63 famp = subprocess.Popen(
65 stdout = subprocess.PIPE,
66 args = ['perl','-we',perl_script]
68 (output, dummy) = famp.communicate()
71 sock = socket.fromfd(fd, family, 0)
73 print("[%s] %d sockinfo" % (tdonor.pid, there), end='')
74 for f in (lambda: socket.AddressFamily(family).name,
75 lambda: repr(sock.getsockname()),
76 lambda: repr(sock.getpeername())):
78 except Exception as e: info = repr(e)
79 print("\t", info, sep='', end='')
84 def permute_fds_for_exec():
85 actual_map = { info[0]: intent for intent, info in fdmap.items }
86 for intent, (actual, tdonor, intarget) in fdmap.items():
87 if intent is not None:
88 in_way = actual_map.get(intent)
89 if in_way is not None:
90 moved = os.dup(intent)
93 def implement_exec(argl):
94 if donor is not None: donor.detach()
99 if donor is not None: donor.detach()
100 donor = fishdescriptor.fish.Donor(pid)
102 def ocb_set_donor(option, opt, value, parser):
105 ov = optparse.Values()
108 def arg_matches(regexp):
110 m = re.search(regexp, arg)
113 op = optparse.OptionParser(usage=usage)
115 op.disable_interspersed_args()
116 op.add_option('-p','--pid', type='int', action='callback',
117 callback='ocb_set_donor')
123 (ov, args) = op.parse_args(args=args, values=ov)
124 if not len(args): break
128 if donor is not None:
130 elif arg_matches(r'^(?:(\d+)=)?(\d+)?$'):
131 (here, there) = m.groups()
132 here = None if here is None else int(here)
134 pending.append = (here,there)
137 op.error("exec needs command to run")
140 elif arg = 'sockinfo':
141 if last_here is None:
142 op.error('sockinfo needs a prior fd spec')
144 implement_sockinfo(last_here)
146 op.error("unknown argument/option `%s'" % arg)
151 there = int(m.group[1])
152 here = None if m.group
154 ,here) = map(int, m.groups())
157 pid = int(sys.argv[1])
158 fds = [int(x) for x in sys.argv[2:]]
160 d = fishdescriptor.fish.Donor(pid)