%PDF- %PDF-
Direktori : /lib/python3/dist-packages/execnet/ |
Current File : //lib/python3/dist-packages/execnet/gateway_io.py |
# -*- coding: utf-8 -*- """ execnet io initialization code creates io instances used for gateway io """ import os import shlex import sys try: from execnet.gateway_base import Popen2IO, Message except ImportError: from __main__ import Popen2IO, Message from functools import partial class Popen2IOMaster(Popen2IO): def __init__(self, args, execmodel): self.popen = p = execmodel.PopenPiped(args) Popen2IO.__init__(self, p.stdin, p.stdout, execmodel=execmodel) def wait(self): try: return self.popen.wait() except OSError: pass # subprocess probably dead already def kill(self): killpopen(self.popen) def killpopen(popen): try: if hasattr(popen, "kill"): popen.kill() else: killpid(popen.pid) except EnvironmentError: sys.stderr.write("ERROR killing: %s\n" % (sys.exc_info()[1])) sys.stderr.flush() def killpid(pid): if hasattr(os, "kill"): os.kill(pid, 15) elif sys.platform == "win32" or getattr(os, "_name", None) == "nt": import ctypes PROCESS_TERMINATE = 1 handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, pid) ctypes.windll.kernel32.TerminateProcess(handle, -1) ctypes.windll.kernel32.CloseHandle(handle) else: raise EnvironmentError("no method to kill {}".format(pid)) popen_bootstrapline = "import sys;exec(eval(sys.stdin.readline()))" def shell_split_path(path): """ Use shell lexer to split the given path into a list of components, taking care to handle Windows' '\' correctly. """ if sys.platform.startswith("win"): # replace \\ by / otherwise shlex will strip them out path = path.replace("\\", "/") return shlex.split(path) def popen_args(spec): args = shell_split_path(spec.python) if spec.python else [sys.executable] args.append("-u") if spec is not None and spec.dont_write_bytecode: args.append("-B") # Slight gymnastics in ordering these arguments because CPython (as of # 2.7.1) ignores -B if you provide `python -c "something" -B` args.extend(["-c", popen_bootstrapline]) return args def ssh_args(spec): # NOTE: If changing this, you need to sync those changes to vagrant_args # as well, or, take some time to further refactor the commonalities of # ssh_args and vagrant_args. remotepython = spec.python or "python" args = ["ssh", "-C"] if spec.ssh_config is not None: args.extend(["-F", str(spec.ssh_config)]) args.extend(spec.ssh.split()) remotecmd = '{} -c "{}"'.format(remotepython, popen_bootstrapline) args.append(remotecmd) return args def vagrant_ssh_args(spec): # This is the vagrant-wrapped version of SSH. Unfortunately the # command lines are incompatible to just channel through ssh_args # due to ordering/templating issues. # NOTE: This should be kept in sync with the ssh_args behaviour. # spec.vagrant is identical to spec.ssh in that they both carry # the remote host "address". remotepython = spec.python or "python" args = ["vagrant", "ssh", spec.vagrant_ssh, "--", "-C"] if spec.ssh_config is not None: args.extend(["-F", str(spec.ssh_config)]) remotecmd = '{} -c "{}"'.format(remotepython, popen_bootstrapline) args.extend([remotecmd]) return args def create_io(spec, execmodel): if spec.popen: args = popen_args(spec) return Popen2IOMaster(args, execmodel) if spec.ssh: args = ssh_args(spec) io = Popen2IOMaster(args, execmodel) io.remoteaddress = spec.ssh return io if spec.vagrant_ssh: args = vagrant_ssh_args(spec) io = Popen2IOMaster(args, execmodel) io.remoteaddress = spec.vagrant_ssh return io # # Proxy Gateway handling code # # master: proxy initiator # forwarder: forwards between master and sub # sub: sub process that is proxied to the initiator RIO_KILL = 1 RIO_WAIT = 2 RIO_REMOTEADDRESS = 3 RIO_CLOSE_WRITE = 4 class ProxyIO(object): """A Proxy IO object allows to instantiate a Gateway through another "via" gateway. A master:ProxyIO object provides an IO object effectively connected to the sub via the forwarder. To achieve this, master:ProxyIO interacts with forwarder:serve_proxy_io() which itself instantiates and interacts with the sub. """ def __init__(self, proxy_channel, execmodel): # after exchanging the control channel we use proxy_channel # for messaging IO self.controlchan = proxy_channel.gateway.newchannel() proxy_channel.send(self.controlchan) self.iochan = proxy_channel self.iochan_file = self.iochan.makefile("r") self.execmodel = execmodel def read(self, nbytes): return self.iochan_file.read(nbytes) def write(self, data): return self.iochan.send(data) def _controll(self, event): self.controlchan.send(event) return self.controlchan.receive() def close_write(self): self._controll(RIO_CLOSE_WRITE) def kill(self): self._controll(RIO_KILL) def wait(self): return self._controll(RIO_WAIT) @property def remoteaddress(self): return self._controll(RIO_REMOTEADDRESS) def __repr__(self): return "<RemoteIO via {}>".format(self.iochan.gateway.id) class PseudoSpec: def __init__(self, vars): self.__dict__.update(vars) def __getattr__(self, name): return None def serve_proxy_io(proxy_channelX): execmodel = proxy_channelX.gateway.execmodel log = partial( proxy_channelX.gateway._trace, "serve_proxy_io:%s" % proxy_channelX.id ) spec = PseudoSpec(proxy_channelX.receive()) # create sub IO object which we will proxy back to our proxy initiator sub_io = create_io(spec, execmodel) control_chan = proxy_channelX.receive() log("got control chan", control_chan) # read data from master, forward it to the sub # XXX writing might block, thus blocking the receiver thread def forward_to_sub(data): log("forward data to sub, size %s" % len(data)) sub_io.write(data) proxy_channelX.setcallback(forward_to_sub) def controll(data): if data == RIO_WAIT: control_chan.send(sub_io.wait()) elif data == RIO_KILL: control_chan.send(sub_io.kill()) elif data == RIO_REMOTEADDRESS: control_chan.send(sub_io.remoteaddress) elif data == RIO_CLOSE_WRITE: control_chan.send(sub_io.close_write()) control_chan.setcallback(controll) # write data to the master coming from the sub forward_to_master_file = proxy_channelX.makefile("w") # read bootstrap byte from sub, send it on to master log("reading bootstrap byte from sub", spec.id) initial = sub_io.read(1) assert initial == "1".encode("ascii"), initial log("forwarding bootstrap byte from sub", spec.id) forward_to_master_file.write(initial) # enter message forwarding loop while True: try: message = Message.from_io(sub_io) except EOFError: log("EOF from sub, terminating proxying loop", spec.id) break message.to_io(forward_to_master_file) # proxy_channelX will be closed from remote_exec's finalization code if __name__ == "__channelexec__": serve_proxy_io(channel) # noqa