Example #1
0
 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()
Example #2
0
 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()
Example #3
0
    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)
Example #4
0
 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()
Example #5
0
 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()
Example #6
0
    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()
Example #7
0
 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)
Example #8
0
    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)
Example #9
0
 def __init__(self):
     abstract.FileDescriptor.__init__(self)
     self.fileno = sys.__stdout__.fileno
     fdesc.setNonBlocking(self.fileno())
Example #10
0
    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)
Example #11
0
    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)