class StandardIO(object):
    implements(interfaces.ITransport, interfaces.IProducer,
               interfaces.IConsumer, interfaces.IHalfCloseableDescriptor)
    _reader = None
    _writer = None
    disconnected = False
    disconnecting = False

    def __init__(self, proto, stdin=0, stdout=1):
        from twisted.internet import reactor
        self.protocol = proto

        self._writer = process.ProcessWriter(reactor, self, 'write', stdout)
        try:
            self._writer.startReading()
        except IOError, e:
            if e.errno == errno.EPERM:
                # epoll will reject certain file descriptors by raising
                # EPERM.  Most commonly, this means stdout was redirected to
                # a regular file.
                raise RuntimeError(
                    "This reactor does not support this type of file "
                    "descriptor (fd %d, mode %d) (for example, epollreactor "
                    "does not support normal files.  See #4429)." %
                    (stdout, os.fstat(stdout).st_mode))
            raise

        self._reader = process.ProcessReader(reactor, self, 'read', stdin)
        self._reader.startReading()
        self.protocol.makeConnection(self)
Beispiel #2
0
    def __init__(self, proto, stdin=0, stdout=1, reactor=None):
        if reactor is None:
            from twisted.internet import reactor
        self.protocol = proto

        self._writer = process.ProcessWriter(reactor, self, "write", stdout)
        self._reader = process.ProcessReader(reactor, self, "read", stdin)
        self._reader.startReading()
        self.protocol.makeConnection(self)
Beispiel #3
0
    def __init__(self, proto, stdin=0, stdout=1):
        from twisted.internet import reactor
        self.protocol = proto

        self._reader = process.ProcessReader(reactor, self, 'read', stdin)
        self._reader.startReading()
        self._writer = process.ProcessWriter(reactor, self, 'write', stdout)
        self._writer.startReading()
        self.protocol.makeConnection(self)
Beispiel #4
0
 def connectionMade(self):
     debug("outgoing connection made")
     self.reader = process.ProcessReader(self.reactor, self, "in", 0)
Beispiel #5
0
    def __init__(self,
                 reactor,
                 command,
                 args,
                 environment,
                 path,
                 proto,
                 uid=None,
                 gid=None,
                 childFDs=None,
                 sessionLeader=False,
                 timeout=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 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
            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
            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)
                # Make a session/process group leader if requested
                if sessionLeader: self._setupSession()
                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

        if timeout:
            self.timeoutCall = reactor.callLater(timeout, self._processTimeout)

        self.proto = proto

        # 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 = process.ProcessReader(reactor, self, childFD,
                                               parentFD)
                self.pipes[childFD] = reader

            if childFDs[childFD] == "w":
                writer = process.ProcessWriter(reactor,
                                               self,
                                               childFD,
                                               parentFD,
                                               forceReadHack=True)
                self.pipes[childFD] = writer

        try:
            # the 'transport' is used for some compatibility methods
            if self.proto is not None:
                self.proto.makeConnection(self)
        except:
            log.err()
        process.registerReapProcessHandler(self.pid, self)