示例#1
0
    def test_registerReapProcessHandler(self):
        process.registerReapProcessHandler(None, None)

        [error] = self.flushLoggedErrors()
        self.assertEqual(
            type(error.value),
            self.expected_type,
            "Wrong error type logged",
        )
        self.assertEqual(
            str(error.value),
            self.expected_message,
            "Wrong error message logged",
        )
示例#2
0
    def __init__(self, reactor, executable, args, environment, path, protocol, name):
        abstract.FileDescriptor.__init__(self, reactor)
        process._BaseProcess.__init__(self, protocol)

        masterfd, slavefd = pty.openpty()
        ttyname = os.ttyname(slavefd)
        uid=None
        gid=None


        try:
            self._fork(path, uid, gid, executable, args, environment,
                       masterfd=masterfd, slavefd=slavefd)
        except:
            os.close(masterfd)
            os.close(slavefd)
            raise

        self._masterfd = masterfd
        self._slavefd = slavefd
        self._args = args
        self._environment = environment
        self._path = path
        self._protocol = protocol
        self._ttyname = ttyname
        self._executable = executable
        
        # we are now in parent process:
        os.close(slavefd)
        fdesc.setNonBlocking(masterfd)
        self.fd = masterfd
        self.startReading()
        self.connected = 1
        self.status = -1

        self._info = info.Info(pid=self.pid,
                               name=name,
                               type='proc',
                               executable=executable)

        try:
            self.proto.makeConnection(self)
        except:
            log.err()
        process.registerReapProcessHandler(self.pid, self)
示例#3
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)