Example #1
0
 def _setupTree1(self):
     self._tmpd = tempfile.mkdtemp(prefix='hotwiretest')
     os.mkdir(path_join(self._tmpd, 'testdir'))
     if is_unix():
         self._test_exe = 'testf'
     elif is_windows():
         self._test_exe = 'testf.exe'
     self._test_exe_path = path_join(self._tmpd, self._test_exe)
     open(self._test_exe_path, 'w').close()
     if is_unix():
         os.chmod(self._test_exe_path, 744)            
     os.mkdir(path_join(self._tmpd, 'dir with spaces'))
Example #2
0
def load():
    if is_unix():
        import hotwire_ui.adaptors.aliases_unix
        fs = Filesystem.getInstance()
        if fs.executable_on_path('hotwire-ssh'):
            import hotwire_ui.adaptors.ssh
        if fs.executable_on_path('hotwire-sudo'):
            import hotwire_ui.adaptors.sudo
        import hotwire.sysdep.unix_completers

    import hotwire_ui.adaptors.edit
    import hotwire_ui.adaptors.view
Example #3
0
 def _setupTree2(self):
     self._setupTree1()
     if is_unix(): 
         self._test_exe2_path = path_join(self._tmpd, 'testf2')
         open(self._test_exe2_path, 'w').close()
         os.chmod(self._test_exe2_path, 744)
     elif is_windows():
         self._test_exe2_path = path_join(self._tmpd, 'testf2.exe')
         open(self._test_exe2_path, 'w').close()
     open(path_join(self._tmpd, 'f3test'), 'w').close()
     open(path_join(self._tmpd, 'otherfile'), 'w').close()
     os.mkdir(path_join(self._tmpd, 'testdir2'))
     open(path_join(self._tmpd, 'testdir2', 'blah'), 'w').close()
     open(path_join(self._tmpd, 'testdir2', 'moo'), 'w').close()
     os.mkdir(path_join(self._tmpd, 'testdir2', 'moodir'))    
Example #4
0
    def execute(self, context, args, in_opt_format=None, out_opt_format=None):
        # This function is complex.  There are two major variables.  First,
        # are we on Unix or Windows?  This is effectively determined by
        # pty_available, though I suppose some Unixes might not have ptys.
        # Second, out_opt_format tells us whether we want to stream the 
        # output as lines (out_opt_format is None), or as unbuffered byte chunks
        # (determined by bytearray/chunked).  There is also a special hack
        # x-filedescriptor/special where we pass along a file descriptor from
        # the subprocess; this is used in unicode.py to directly read the output.
        
        using_pty_out = pty_available and (out_opt_format not in (None, 'x-unix-pipe-file-object/special'))
        using_pty_in = pty_available and (in_opt_format is None) and \
                       context.input_is_first and hasattr(context.input, 'connect')
        _logger.debug("using pty in: %s out: %s", using_pty_in, using_pty_out)
        # TODO - we need to rework things so that we allocate only one pty per pipeline.
        # In the very common case of exactly one command, this doesn't matter, but 
        # allocating two ptys will probably bite us in odd ways if someone does create
        # a pipeline.  Maybe have a context.request_pty() function?
        if using_pty_in or using_pty_out:
            # We create a pseudo-terminal to ensure that the subprocess is line-buffered.
            # Yes, this is gross, but as far as I know there is no other way to
            # control the buffering used by subprocesses.
            (master_fd, slave_fd) = pty.openpty()
                      
            # Set the terminal to not do any processing; if you change this, you'll also
            # need to update unicode.py most likely.
            attrs = termios.tcgetattr(master_fd)
            attrs[1] = attrs[1] & (~termios.OPOST)
            termios.tcsetattr(master_fd, termios.TCSANOW, attrs)            
            
            _logger.debug("allocated pty fds %d %d", master_fd, slave_fd)
            if using_pty_out:
                stdout_target = slave_fd
            else:
                stdout_target = subprocess.PIPE
            if context.input is None:
                stdin_target = None                
            if using_pty_in:
                stdin_target = slave_fd
            elif in_opt_format == 'x-unix-pipe-file-object/special':
                stdin_target = next(iter(context.input))                
            else:
                stdin_target = subprocess.PIPE
            _logger.debug("using stdin target: %r", stdin_target)                
            context.attribs['master_fd'] = master_fd
        else:
            _logger.debug("no pty available or non-chunked output, not allocating fds")
            (master_fd, slave_fd) = (None, None)
            stdout_target = subprocess.PIPE
            if context.input is None:
                stdin_target = None
            elif in_opt_format == 'x-unix-pipe-file-object/special':
                stdin_target = next(iter(context.input))                
            else:
                stdin_target = subprocess.PIPE
            _logger.debug("using stdin target: %r", stdin_target)

        subproc_args = {'bufsize': 0,
                        'stdin': stdin_target,
                        'stdout': stdout_target,
                        'stderr': subprocess.STDOUT,
                        'cwd': context.cwd}
        
        fs_encoding = sys.getfilesystemencoding()
        stdin_encoding = sys.stdin.encoding
        _logger.debug("recoding path to %r, args to %r", fs_encoding, stdin_encoding)
        # We need to encode arguments to the system encoding because subprocess.py won't do it for us.
        if fs_encoding is not None:
            args[0] = args[0].encode(fs_encoding)
        if stdin_encoding is not None:
            args[1:] = [x.encode(stdin_encoding) for x in args[1:]]
        
        if is_windows():
            subproc_args['universal_newlines'] = True
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            import win32con
            startupinfo.wShowWindow = win32con.SW_HIDE            
        elif is_unix():
            subproc_args['close_fds'] = True
            # Support freedesktop.org startup notification
            # http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt
            if context.gtk_event_time:
                env = dict(os.environ)
                env['DESKTOP_STARTUP_ID'] = 'hotwire%d_TIME%d' % (os.getpid(), context.gtk_event_time,)
                subproc_args['env'] = env
            
            def preexec():
                os.setsid()                        
                if using_pty_out and hasattr(termios, 'TIOCSCTTY'):
                    # Set our controlling TTY
                    fcntl.ioctl(1, termios.TIOCSCTTY, '')
                # We ignore SIGHUP by default, because some broken programs expect that the lifetime
                # of subcommands is tied to an open window, instead of until when the toplevel
                # process exits.
                signal.signal(signal.SIGHUP, signal.SIG_IGN)
            subproc_args['preexec_fn'] = preexec
        else:
            assert(False)    
        subproc = subprocess.Popen(args, **subproc_args)
        if not subproc.pid:
            if master_fd is not None:
                os.close(master_fd)
            if slave_fd is not None:
                os.close(slave_fd)
            raise ValueError('Failed to execute %s' % (args[0],))
        context.attribs['pid'] = subproc.pid
        if using_pty_in or using_pty_out:
            os.close(slave_fd)            
        context.status_notify('pid %d' % (context.attribs['pid'],))
        if in_opt_format == 'x-unix-pipe-file-object/special':
            stdin_target.close()
            # If we were passed a file descriptor from another SysBuiltin, close it here;
            # it's now owned by the child.            
        elif context.input:
            if using_pty_in:
                stdin_stream = BareFdStreamWriter(master_fd)
            else:
                stdin_stream = subproc.stdin
            # FIXME hack - need to rework input streaming                
            if context.input_is_first and hasattr(context.input, 'connect'):
                context.attribs['input_connected'] = True
                context.input.connect(self.__on_input, stdin_stream)
            else:
                MiniThreadPool.getInstance().run(self.__inputwriter, args=(context.input, stdin_stream))
        if using_pty_out:
            stdout_read = None
            stdout_fd = master_fd
        else:
            stdout_read = subproc.stdout
            stdout_fd = subproc.stdout.fileno()
        if out_opt_format is None:
            for line in SysBuiltin.__unbuffered_readlines(stdout_read):
                yield line
        elif out_opt_format == 'bytearray/chunked':     
            try:
                for buf in SysBuiltin.__unbuffered_read_pipe(stream=stdout_read, fd=stdout_fd):
                    yield buf
            except OSError as e:
                pass
        elif out_opt_format == 'x-unix-pipe-file-object/special':
            yield subproc.stdout
        elif out_opt_format == 'x-filedescriptor/special':
            context.attribs['master_fd_passed'] = True            
            yield stdout_fd
        else:
            assert(False)
        retcode = subproc.wait()
        if retcode >= 0:
            retcode_str = '%d' % (retcode,)
        else:
            retcode_str = _('signal %d') % (abs(retcode),)
        context.status_notify(_('Exit %s') % (retcode_str,))
Example #5
0
try:
    import pty, termios, fcntl
    pty_available = True
except:
    pty_available = False

import hotwire
from hotwire.logutil import log_except
from hotwire.text import MarkupText
from hotwire.async import MiniThreadPool
from hotwire.externals.singletonmixin import Singleton
from hotwire.builtin import Builtin, BuiltinRegistry, InputStreamSchema, OutputStreamSchema, MultiArgSpec
from hotwire.sysdep import is_windows, is_unix
from hotwire.sysdep.proc import ProcessManager

if is_unix():
    import signal

_logger = logging.getLogger("hotwire.builtin.Sys")

class SystemCompleters(dict, Singleton):
    def __init__(self):
        super(SystemCompleters, self).__init__()
        
# This object is necessary because we don't want the file object
# to close the pty FD when it's unreffed.
class BareFdStreamWriter(object):
    def __init__(self, fd):
        self.fd = fd
        
    def write(self, obj):
Example #6
0
 def get_opt_formats(self):
     if is_unix():
         return ['x-filedescriptor/special', 'bytearray/chunked']
     else:
         return ['bytearray/chunked']
Example #7
0
        self.cmd = cmd
        self.owner_name = owner_name

    def kill(self):
        raise NotImplementedError()

    def __str__(self):
        return "Process '%s' (%s) of %s" % (self.cmd, self.pid,
                                            self.owner_name)


_module = None
if is_linux():
    import hotwire.sysdep.proc_impl.proc_linux
    _module = hotwire.sysdep.proc_impl.proc_linux
elif is_unix():
    import hotwire.sysdep.proc_impl.proc_unix
    _module = hotwire.sysdep.proc_impl.proc_unix
elif is_windows():
    import hotwire.sysdep.proc_impl.proc_win32
    _module = hotwire.sysdep.proc_impl.proc_win32
else:
    raise NotImplementedError("No Process implemented for %r" %
                              (platform.system(), ))

_instance = None


class ProcessManager(object):
    @staticmethod
    def getInstance():
Example #8
0
    def execute(self, context, args, in_opt_format=None, out_opt_format=None):
        # This function is complex.  There are two major variables.  First,
        # are we on Unix or Windows?  This is effectively determined by
        # pty_available, though I suppose some Unixes might not have ptys.
        # Second, out_opt_format tells us whether we want to stream the
        # output as lines (out_opt_format is None), or as unbuffered byte chunks
        # (determined by bytearray/chunked).  There is also a special hack
        # x-filedescriptor/special where we pass along a file descriptor from
        # the subprocess; this is used in unicode.py to directly read the output.

        using_pty_out = pty_available and (out_opt_format not in (
            None, 'x-unix-pipe-file-object/special'))
        using_pty_in = pty_available and (in_opt_format is None) and \
                       context.input_is_first and hasattr(context.input, 'connect')
        _logger.debug("using pty in: %s out: %s", using_pty_in, using_pty_out)
        # TODO - we need to rework things so that we allocate only one pty per pipeline.
        # In the very common case of exactly one command, this doesn't matter, but
        # allocating two ptys will probably bite us in odd ways if someone does create
        # a pipeline.  Maybe have a context.request_pty() function?
        if using_pty_in or using_pty_out:
            # We create a pseudo-terminal to ensure that the subprocess is line-buffered.
            # Yes, this is gross, but as far as I know there is no other way to
            # control the buffering used by subprocesses.
            (master_fd, slave_fd) = pty.openpty()

            # Set the terminal to not do any processing; if you change this, you'll also
            # need to update unicode.py most likely.
            attrs = termios.tcgetattr(master_fd)
            attrs[1] = attrs[1] & (~termios.OPOST)
            termios.tcsetattr(master_fd, termios.TCSANOW, attrs)

            _logger.debug("allocated pty fds %d %d", master_fd, slave_fd)
            if using_pty_out:
                stdout_target = slave_fd
            else:
                stdout_target = subprocess.PIPE
            if context.input is None:
                stdin_target = None
            if using_pty_in:
                stdin_target = slave_fd
            elif in_opt_format == 'x-unix-pipe-file-object/special':
                stdin_target = next(iter(context.input))
            else:
                stdin_target = subprocess.PIPE
            _logger.debug("using stdin target: %r", stdin_target)
            context.attribs['master_fd'] = master_fd
        else:
            _logger.debug(
                "no pty available or non-chunked output, not allocating fds")
            (master_fd, slave_fd) = (None, None)
            stdout_target = subprocess.PIPE
            if context.input is None:
                stdin_target = None
            elif in_opt_format == 'x-unix-pipe-file-object/special':
                stdin_target = next(iter(context.input))
            else:
                stdin_target = subprocess.PIPE
            _logger.debug("using stdin target: %r", stdin_target)

        subproc_args = {
            'bufsize': 0,
            'stdin': stdin_target,
            'stdout': stdout_target,
            'stderr': subprocess.STDOUT,
            'cwd': context.cwd
        }

        fs_encoding = sys.getfilesystemencoding()
        stdin_encoding = sys.stdin.encoding
        _logger.debug("recoding path to %r, args to %r", fs_encoding,
                      stdin_encoding)
        # We need to encode arguments to the system encoding because subprocess.py won't do it for us.
        if fs_encoding is not None:
            args[0] = args[0].encode(fs_encoding)
        if stdin_encoding is not None:
            args[1:] = [x.encode(stdin_encoding) for x in args[1:]]

        if is_windows():
            subproc_args['universal_newlines'] = True
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            import win32con
            startupinfo.wShowWindow = win32con.SW_HIDE
        elif is_unix():
            subproc_args['close_fds'] = True
            # Support freedesktop.org startup notification
            # http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt
            if context.gtk_event_time:
                env = dict(os.environ)
                env['DESKTOP_STARTUP_ID'] = 'hotwire%d_TIME%d' % (
                    os.getpid(),
                    context.gtk_event_time,
                )
                subproc_args['env'] = env

            def preexec():
                os.setsid()
                if using_pty_out and hasattr(termios, 'TIOCSCTTY'):
                    # Set our controlling TTY
                    fcntl.ioctl(1, termios.TIOCSCTTY, '')
                # We ignore SIGHUP by default, because some broken programs expect that the lifetime
                # of subcommands is tied to an open window, instead of until when the toplevel
                # process exits.
                signal.signal(signal.SIGHUP, signal.SIG_IGN)

            subproc_args['preexec_fn'] = preexec
        else:
            assert (False)
        subproc = subprocess.Popen(args, **subproc_args)
        if not subproc.pid:
            if master_fd is not None:
                os.close(master_fd)
            if slave_fd is not None:
                os.close(slave_fd)
            raise ValueError('Failed to execute %s' % (args[0], ))
        context.attribs['pid'] = subproc.pid
        if using_pty_in or using_pty_out:
            os.close(slave_fd)
        context.status_notify('pid %d' % (context.attribs['pid'], ))
        if in_opt_format == 'x-unix-pipe-file-object/special':
            stdin_target.close()
            # If we were passed a file descriptor from another SysBuiltin, close it here;
            # it's now owned by the child.
        elif context.input:
            if using_pty_in:
                stdin_stream = BareFdStreamWriter(master_fd)
            else:
                stdin_stream = subproc.stdin
            # FIXME hack - need to rework input streaming
            if context.input_is_first and hasattr(context.input, 'connect'):
                context.attribs['input_connected'] = True
                context.input.connect(self.__on_input, stdin_stream)
            else:
                MiniThreadPool.getInstance().run(self.__inputwriter,
                                                 args=(context.input,
                                                       stdin_stream))
        if using_pty_out:
            stdout_read = None
            stdout_fd = master_fd
        else:
            stdout_read = subproc.stdout
            stdout_fd = subproc.stdout.fileno()
        if out_opt_format is None:
            for line in SysBuiltin.__unbuffered_readlines(stdout_read):
                yield line
        elif out_opt_format == 'bytearray/chunked':
            try:
                for buf in SysBuiltin.__unbuffered_read_pipe(
                        stream=stdout_read, fd=stdout_fd):
                    yield buf
            except OSError as e:
                pass
        elif out_opt_format == 'x-unix-pipe-file-object/special':
            yield subproc.stdout
        elif out_opt_format == 'x-filedescriptor/special':
            context.attribs['master_fd_passed'] = True
            yield stdout_fd
        else:
            assert (False)
        retcode = subproc.wait()
        if retcode >= 0:
            retcode_str = '%d' % (retcode, )
        else:
            retcode_str = _('signal %d') % (abs(retcode), )
        context.status_notify(_('Exit %s') % (retcode_str, ))
Example #9
0
 def get_opt_formats(self):
     if is_unix():
         return ['x-filedescriptor/special', 'bytearray/chunked']
     else:
         return ['bytearray/chunked']