X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=fishdescriptor%2Ffishdescriptor;h=8bcb46707c210c8b0436aead2b72aa1b0fb7f5f9;hb=37d98ac0bf952f43a40fd7a276f372ab45fd1a0c;hp=bdddff2a12e380a97971669a76b3603c10b61f89;hpb=6ebf988ecb4d0246f72b544e04e06ac288714126;p=chiark-utils.git diff --git a/fishdescriptor/fishdescriptor b/fishdescriptor/fishdescriptor index bdddff2..8bcb467 100755 --- a/fishdescriptor/fishdescriptor +++ b/fishdescriptor/fishdescriptor @@ -7,9 +7,7 @@ import re donor = None -usage = -''' -fishdescriptor [-p|--pid] ... [-p|--pid ...] +usage = '''fishdescriptor [-p|--pid] ... [-p|--pid ...] s [=] @@ -27,22 +25,22 @@ fishdescriptor [-p|--pid] ... [-p|--pid ...] now attach to , detaching from previous pid ''' -fdmap = { } -# fdmap[here] = (actual_fd_number_here, Donor, target_fd) +pending = [] +# list of (nominal, there) where nominal might be None -last_here = None +fdmap = { } +# fdmap[nominal] = (actual, Donor, there) 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] + actuals = donor.fish([pend[1] for pend in pending]) + assert(len(actuals) == len(pending)) + for (nominal, there), actual in zip(pending, actuals): + overwriting_info = fdmap.get(nominal) + if overwriting_info is not None: os.close(overwriting_info[0]) + fdmap[nominal] = (actual, Donor, there) + +def implmement_sockinfo(nominal): + (actual, tdonor, there) = fdmap[nominal] # 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 @@ -61,7 +59,7 @@ def implmement_sockinfo(here): print $family, "\n" or die $!; ''' famp = subprocess.Popen( - stdin = fd, + stdin = actual, stdout = subprocess.PIPE, args = ['perl','-we',perl_script] ) @@ -82,17 +80,36 @@ def implmement_sockinfo(here): 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] = + actual2intended = { info[0]: nominal for nominal, info in fdmap.items } + # invariant at the start of each loop iteration: + # for each intended (aka `nominal') we have processed: + # relevant open-file is only held in fd intended + # (unless `nominal' is None in which case it is closed) + # for each intended (aka `nominal') we have NOT processed: + # relevant open-file is only held in actual + # where actual = fdmap[nominal][0] + # and where actual2intended[actual] = nominal + # we can rely on processing each intended only once, + # since they're hash keys + # the post-condition is not really a valid state (fdmap + # is nonsense) but we call this function just before exec + for intended, (actual, tdonor, there) in fdmap.items(): + if intended == actual: + continue + if intended is not None: + inway_intended = actual2intended.get(intended) + if inway_intended is not None: + inway_moved = os.dup(intended) + actual2intended[inway_moved] = inway_intended + fdmap[inway_intented][0] = inway_moved + os.dup2(actual, intended) + os.close(actual) def implement_exec(argl): if donor is not None: donor.detach() - + sys.stdout.flush() + permut_fds_for_exec() + os.execvp(argl[0], argl) def set_donor(pid): global donor @@ -105,6 +122,10 @@ def ocb_set_donor(option, opt, value, parser): ov = optparse.Values() def process_args(): + global ov + + m = None + def arg_matches(regexp): nonlocal m m = re.search(regexp, arg) @@ -114,10 +135,10 @@ def process_args(): op.disable_interspersed_args() op.add_option('-p','--pid', type='int', action='callback', - callback='ocb_set_donor') + callback=ocb_set_donor) - args = sys.argv - pending = [] + args = sys.argv[1:] + last_nominal = None while True: (ov, args) = op.parse_args(args=args, values=ov) @@ -128,35 +149,22 @@ def process_args(): 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) + (nominal, there) = m.groups() + nominal = None if nominal is None else int(nominal) there = int(there) - pending.append = (here,there) - elif arg = 'exec': + pending.append((nominal,there)) + last_nominal = nominal + 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: + elif arg == 'sockinfo': + if last_nominal is None: op.error('sockinfo needs a prior fd spec') implement_pending() - implement_sockinfo(last_here) + implement_sockinfo(last_nominal) 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:]] - -d = fishdescriptor.fish.Donor(pid) -r = d.fish(fds) -print(repr(r)) +process_args()