def __init__(self, reactor, proc, name, fileno): """Initialize, specifying a process to connect to. """ abstract.FileDescriptor.__init__(self, reactor) fdesc.setNonBlocking(fileno) self.proc = proc self.name = name self.fd = fileno self.startReading()
def __init__(self, protocol): """Create me with a protocol. This will fail if a StandardIO has already been instantiated. """ abstract.FileDescriptor.__init__(self) global _stdio_in_use if _stdio_in_use: raise RuntimeError, "Standard IO already in use." _stdio_in_use = 1 self.fileno = sys.__stdin__.fileno fdesc.setNonBlocking(self.fileno()) self.protocol = protocol self.startReading() self.writer = StandardIOWriter() self.protocol.makeConnection(self)
def __init__(self, reactor, proc, name, fileno, forceReadHack=False): """Initialize, specifying a Process instance to connect to. """ abstract.FileDescriptor.__init__(self, reactor) fdesc.setNonBlocking(fileno) self.proc = proc self.name = name self.fd = fileno if forceReadHack: self.enableReadHack = True else: try: os.read(self.fileno(), 0) except OSError: self.enableReadHack = True if self.enableReadHack: self.startReading()
def __init__(self, reactor, proc, name, fileno, forceReadHack=False): """Initialize, specifying a Process instance to connect to. """ abstract.FileDescriptor.__init__(self, reactor) fdesc.setNonBlocking(fileno) self.proc = proc self.name = name self.fd = fileno if forceReadHack: self.enableReadHack = True else: # Detect if this fd is actually a write-only fd. If it's # valid to read, don't try to detect closing via read. # This really only means that we cannot detect a TTY's write # pipe being closed. try: os.read(self.fileno(), 0) except OSError: # It's a write-only pipe end, enable hack self.enableReadHack = True if self.enableReadHack: self.startReading()
def __init__(self, reactor, command, args, environment, path, proto, uid=None, gid=None, usePTY=None): """Spawn an operating-system process. This is where the hard work of disconnecting all currently open files / forking / executing the new process happens. (This is executed automatically when a Process is instantiated.) This will also run the subprocess as a given user ID and group ID, if specified. (Implementation Note: this doesn't support all the arcane nuances of setXXuid on UNIX: it will assume that either your effective or real UID is 0.) """ if not pty and type(usePTY) not in (types.ListType, types.TupleType): raise NotImplementedError, "cannot use PTYProcess on platforms without the pty module." abstract.FileDescriptor.__init__(self, reactor) settingUID = (uid is not None) or (gid is not None) if settingUID: curegid = os.getegid() currgid = os.getgid() cureuid = os.geteuid() curruid = os.getuid() if uid is None: uid = cureuid if gid is None: gid = curegid os.setuid(0) os.setgid(0) if type(usePTY) in (types.TupleType, types.ListType): masterfd, slavefd, ttyname = usePTY else: masterfd, slavefd = pty.openpty() ttyname = os.ttyname(slavefd) pid = os.fork() self.pid = pid if pid == 0: # pid is 0 in the child process try: sys.settrace(None) os.close(masterfd) if hasattr(termios, 'TIOCNOTTY'): try: fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) except OSError: pass else: try: fcntl.ioctl(fd, termios.TIOCNOTTY, '') except: pass os.close(fd) os.setsid() if hasattr(termios, 'TIOCSCTTY'): fcntl.ioctl(slavefd, termios.TIOCSCTTY, '') for fd in range(3): if fd != slavefd: os.close(fd) os.dup2(slavefd, 0) # stdin os.dup2(slavefd, 1) # stdout os.dup2(slavefd, 2) # stderr if path: os.chdir(path) for fd in range(3, 256): try: os.close(fd) except: pass if settingUID: switchUID(uid, gid) os.execvpe(command, args, environment) except: stderr = os.fdopen(1, 'w') stderr.write("Upon execvpe %s %s in environment %s:\n" % (command, str(args), "id %s" % id(environment))) traceback.print_exc(file=stderr) stderr.flush() os._exit(1) assert pid!=0 os.close(slavefd) fdesc.setNonBlocking(masterfd) self.fd=masterfd self.startReading() self.connected = 1 self.proto = proto self.lostProcess = 0 self.status = -1 try: self.proto.makeConnection(self) except: log.err() registerReapProcessHandler(self.pid, self)
def __init__(self, reactor, command, args, environment, path, proto, uid=None, gid=None, usePTY=None): """Spawn an operating-system process. This is where the hard work of disconnecting all currently open files / forking / executing the new process happens. (This is executed automatically when a Process is instantiated.) This will also run the subprocess as a given user ID and group ID, if specified. (Implementation Note: this doesn't support all the arcane nuances of setXXuid on UNIX: it will assume that either your effective or real UID is 0.) """ if not pty and type(usePTY) not in (types.ListType, types.TupleType): # no pty module and we didn't get a pty to use raise NotImplementedError, "cannot use PTYProcess on platforms without the pty module." abstract.FileDescriptor.__init__(self, reactor) settingUID = (uid is not None) or (gid is not None) if settingUID: curegid = os.getegid() currgid = os.getgid() cureuid = os.geteuid() curruid = os.getuid() if uid is None: uid = cureuid if gid is None: gid = curegid # prepare to change UID in subprocess os.setuid(0) os.setgid(0) if type(usePTY) in (types.TupleType, types.ListType): masterfd, slavefd, ttyname = usePTY else: masterfd, slavefd = pty.openpty() ttyname = os.ttyname(slavefd) pid = os.fork() self.pid = pid if pid == 0: # pid is 0 in the child process try: sys.settrace(None) os.close(masterfd) if hasattr(termios, 'TIOCNOTTY'): try: fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) except OSError: pass else: try: fcntl.ioctl(fd, termios.TIOCNOTTY, '') except: pass os.close(fd) os.setsid() if hasattr(termios, 'TIOCSCTTY'): fcntl.ioctl(slavefd, termios.TIOCSCTTY, '') for fd in range(3): if fd != slavefd: os.close(fd) os.dup2(slavefd, 0) # stdin os.dup2(slavefd, 1) # stdout os.dup2(slavefd, 2) # stderr if path: os.chdir(path) for fd in range(3, 256): try: os.close(fd) except: pass # set the UID before I actually exec the process if settingUID: switchUID(uid, gid) os.execvpe(command, args, environment) except: stderr = os.fdopen(1, 'w') stderr.write("Upon execvpe %s %s in environment %s:\n" % (command, str(args), "id %s" % id(environment))) traceback.print_exc(file=stderr) stderr.flush() os._exit(1) assert pid != 0 os.close(slavefd) fdesc.setNonBlocking(masterfd) self.fd = masterfd self.startReading() self.connected = 1 self.proto = proto self.lostProcess = 0 self.status = -1 try: self.proto.makeConnection(self) except: log.err() registerReapProcessHandler(self.pid, self)
def __init__(self): abstract.FileDescriptor.__init__(self) self.fileno = sys.__stdout__.fileno fdesc.setNonBlocking(self.fileno())
def __init__(self, reactor, command, args, environment, path, proto, uid=None, gid=None, childFDs=None): """Spawn an operating-system process. This is where the hard work of disconnecting all currently open files / forking / executing the new process happens. (This is executed automatically when a Process is instantiated.) This will also run the subprocess as a given user ID and group ID, if specified. (Implementation Note: this doesn't support all the arcane nuances of setXXuid on UNIX: it will assume that either your effective or real UID is 0.) @param childFDs: a dictionary mapping fd_in_child -> current_fd_in_parent/'r'/'w' If the value is a number, it specifies one of the parent's fds that will be remapped to the child's fd. This is useful for things like inetd and shell-like file redirection. If it is the string 'r', a pipe will be created and attached to the child at that fd number, and the parent will be able to read from the pipe. This is useful for the child's stdout and stderr. If it is the string 'w', a pipe will be created and attached, and the parent will be able to write into that pipe. This is useful for the child's stdin. If childFDs is not passed, the default behaviour is to use a mapping that opens the usual stdin/stdout/stderr pipes. """ if not proto: assert 'r' not in childFDs.values() assert 'w' not in childFDs.values() if not signal.getsignal(signal.SIGCHLD): log.msg("spawnProcess called, but the SIGCHLD handler is not " + "installed. This probably means you have not yet " + "called reactor.run, or called " + "reactor.run(installSignalHandler=0). You will probably " + "never see this process finish, and it may become a " + "zombie process.") # if you see this message during a unit test, look in # test-standard.xhtml or twisted.test.test_process.SignalMixin # for a workaround self.lostProcess = False settingUID = (uid is not None) or (gid is not None) if settingUID: curegid = os.getegid() currgid = os.getgid() cureuid = os.geteuid() curruid = os.getuid() if uid is None: uid = cureuid if gid is None: gid = curegid # prepare to change UID in subprocess os.setuid(0) os.setgid(0) self.pipes = {} # keys are childFDs, we can sense them closing # values are ProcessReader/ProcessWriters helpers = {} # keys are childFDs # values are parentFDs if childFDs is None: childFDs = {0: "w", # we write to the child's stdin 1: "r", # we read from their stdout 2: "r", # and we read from their stderr } debug = self.debug if debug: print "childFDs", childFDs # fdmap.keys() are filenos of pipes that are used by the child. fdmap = {} # maps childFD to parentFD for childFD, target in childFDs.items(): if debug: print "[%d]" % childFD, target if target == "r": # we need a pipe that the parent can read from readFD, writeFD = os.pipe() if debug: print "readFD=%d, writeFD%d" % (readFD, writeFD) fdmap[childFD] = writeFD # child writes to this helpers[childFD] = readFD # parent reads from this fdesc.setNonBlocking(readFD) elif target == "w": # we need a pipe that the parent can write to readFD, writeFD = os.pipe() if debug: print "readFD=%d, writeFD=%d" % (readFD, writeFD) fdmap[childFD] = readFD # child reads from this helpers[childFD] = writeFD # parent writes to this fdesc.setNonBlocking(writeFD) else: assert type(target) == int, '%r should be an int' % (target,) fdmap[childFD] = target # parent ignores this if debug: print "fdmap", fdmap if debug: print "helpers", helpers # the child only cares about fdmap.values() self.pid = os.fork() if self.pid == 0: # pid is 0 in the child process # do not put *ANY* code outside the try block. The child process # must either exec or _exit. If it gets outside this block (due # to an exception that is not handled here, but which might be # handled higher up), there will be two copies of the parent # running in parallel, doing all kinds of damage. # After each change to this code, review it to make sure there # are no exit paths. try: # stop debugging, if I am! I don't care anymore! sys.settrace(None) # close all parent-side pipes self._setupChild(fdmap) self._execChild(path, settingUID, uid, gid, command, args, environment) except: # If there are errors, bail and try to write something # descriptive to stderr. # XXX: The parent's stderr isn't necessarily fd 2 anymore, or # even still available # XXXX: however even libc assumes write(2,err) is a useful # thing to attempt try: stderr = os.fdopen(2,'w') stderr.write("Upon execvpe %s %s in environment %s\n:" % (command, str(args), "id %s" % id(environment))) traceback.print_exc(file=stderr) stderr.flush() for fd in range(3): os.close(fd) except: pass # make *sure* the child terminates # Did you read the comment about not adding code here? os._exit(1) # we are the parent if settingUID: os.setregid(currgid, curegid) os.setreuid(curruid, cureuid) self.status = -1 # this records the exit status of the child # arrange for the parent-side pipes to be read and written for childFD, parentFD in helpers.items(): os.close(fdmap[childFD]) if childFDs[childFD] == "r": reader = ProcessReader(reactor, self, childFD, parentFD) reader.proto = proto self.pipes[childFD] = reader reader.startReading() if childFDs[childFD] == "w": writer = ProcessWriter(reactor, self, childFD, parentFD) writer.proto = proto self.pipes[childFD] = writer # we do startReading here to watch for EOF. We won't do an # actual .startWriting until some data has been written to # the transmit buffer. writer.startReading() self.proto = proto try: # the 'transport' is used for some compatibility methods if self.proto is not None: self.proto.makeConnection(self) except: log.err() registerReapProcessHandler(self.pid, self)
def __init__(self, reactor, command, args, environment, path, proto, uid=None, gid=None): """Spawn an operating-system process. This is where the hard work of disconnecting all currently open files / forking / executing the new process happens. (This is executed automatically when a Process is instantiated.) This will also run the subprocess as a given user ID and group ID, if specified. (Implementation Note: this doesn't support all the arcane nuances of setXXuid on UNIX: it will assume that either your effective or real UID is 0.) """ abstract.FileDescriptor.__init__(self, reactor) settingUID = (uid is not None) or (gid is not None) if settingUID: curegid = os.getegid() currgid = os.getgid() cureuid = os.geteuid() curruid = os.getuid() if uid is None: uid = cureuid if gid is None: gid = curegid # prepare to change UID in subprocess os.setuid(0) os.setgid(0) stdout_read, stdout_write = os.pipe() stderr_read, stderr_write = os.pipe() stdin_read, stdin_write = os.pipe() self.pid = os.fork() if self.pid == 0: # pid is 0 in the child process # stop debugging, if I am! I don't care anymore! sys.settrace(None) # Destroy my stdin / stdout / stderr (in that order) try: os.dup2(stdin_read, 0) os.dup2(stdout_write, 1) os.dup2(stderr_write, 2) # XXX TODO FIXME: 256 is a magic number here; really we need a # way of saying "close all open FDs except 0, 1, 2". This will # fail in a surprising and subtle way if the current process # has more than 256 FDs open. On linux this would be # "[os.close(int(fd)) for fd in os.listdir('/proc/self/fd')]" # but I seriously doubt that's portable. for fd in range(3, 256): try: os.close(fd) except: pass if path: os.chdir(path) # set the UID before I actually exec the process if settingUID: switch_uid(uid, gid) os.execvpe(command, args, environment) except: # If there are errors, bail and try to write something # descriptive to stderr. try: stderr = os.fdopen(2,'w') stderr.write("Upon execvpe %s %s in environment %s\n:" % (command, str(args), "id %s" % id(environment))) traceback.print_exc(file=stderr) stderr.flush() for fd in range(3): os.close(fd) except: pass # make *sure* the child terminates os._exit(1) if settingUID: os.setregid(currgid, curegid) os.setreuid(curruid, cureuid) self.status = -1 for fd in stdout_write, stderr_write, stdin_read: os.close(fd) for fd in (stdout_read, stderr_read, stdin_write): fdesc.setNonBlocking(fd) self.stdout = stdout_read # os.fdopen(stdout_read, 'r') self.stderr = stderr_read # os.fdopen(stderr_read, 'r') self.stdin = stdin_write # ok now I really have a fileno() self.writer = ProcessWriter(self) self.writer.startReading() self.err = ProcessError(self) self.err.startReading() self.startReading() self.connected = 1 self.proto = proto try: self.proto.makeConnection(self) except: log.deferr() registerReapProcessHandler(self.pid, self)