Example #1
0
    def testRead(self):
        if posix_.environ.get('EINTR_TEST'):
            # Now we can do kill -TERM PID can get EINTR.
            # Or Ctrl-C for KeyboardInterrupt

            signal.signal(signal.SIGTERM, _Handler)
            log('Hanging on read in pid %d', posix_.getpid())
            posix_.read(0, 1)
Example #2
0
def ReadBytesFromTerminal(fd, n):
    # type: (int, int) -> str

    # silly way to make a copy
    # https://docs.python.org/2/library/termios.html
    orig_attrs = termios.tcgetattr(fd)
    term_attrs = termios.tcgetattr(fd)

    # cast for MyPy.  Each element in termios doesn't have a static type!
    #reveal_type(term_attrs[3])

    a3 = cast(int, term_attrs[3])
    # Disable canonical (buffered) mode.  See `man termios` for an extended
    # discussion.
    term_attrs[3] = a3 & ~termios.ICANON

    chunks = []
    try:
        termios.tcsetattr(fd, termios.TCSANOW, term_attrs)
        # posix.read always returns a single character in unbuffered mode
        while n > 0:
            chunks.append(posix.read(fd, 1))
            n -= 1
    finally:
        termios.tcsetattr(fd, termios.TCSANOW, orig_attrs)

    return ''.join(chunks)
 def Run(self, cmd_val):
     # type: (cmd_value__Argv) -> int
     while True:
         chunk = posix.read(0, 4096)
         if len(chunk) == 0:
             break
         sys.stdout.write(chunk)
     return 0
Example #4
0
    def RunCommandSub(self, node):
        # type: (command_t) -> str

        # Hack for weird $(<file) construct
        if node.tag_() == command_e.Simple:
            simple = cast(command__Simple, node)
            # Detect '< file'
            if (len(simple.words) == 0 and len(simple.redirects) == 1
                    and simple.redirects[0].op.id == Id.Redir_Less):
                # change it to __cat < file
                # note: cmd_eval.py _Dispatch works around lack of spid
                tok = Token(Id.Lit_Chars, runtime.NO_SPID, '__cat')
                cat_word = compound_word([tok])
                # MUTATE the command.Simple node.  This will only be done the first
                # time in the parent process.
                simple.words.append(cat_word)

        p = self._MakeProcess(node,
                              inherit_errexit=self.exec_opts.inherit_errexit())

        r, w = posix.pipe()
        p.AddStateChange(process.StdoutToPipe(r, w))
        _ = p.Start()
        #log('Command sub started %d', pid)

        chunks = []  # type: List[str]
        posix.close(w)  # not going to write
        while True:
            byte_str = posix.read(r, 4096)
            if len(byte_str) == 0:
                break
            chunks.append(byte_str)
        posix.close(r)

        status = p.Wait(self.waiter)

        # OSH has the concept of aborting in the middle of a WORD.  We're not
        # waiting until the command is over!
        if self.exec_opts.more_errexit():
            if self.exec_opts.errexit() and status != 0:
                raise error.ErrExit('Command sub exited with status %d (%r)',
                                    status, NewStr(command_str(node.tag_())))
        else:
            # Set a flag so we check errexit at the same time as bash.  Example:
            #
            # a=$(false)
            # echo foo  # no matter what comes here, the flag is reset
            #
            # Set ONLY until this command node has finished executing.

            # HACK: move this
            self.cmd_ev.check_command_sub_status = True
            self.mem.SetLastStatus(status)

        # Runtime errors test case: # $("echo foo > $@")
        # Why rstrip()?
        # https://unix.stackexchange.com/questions/17747/why-does-shell-command-substitution-gobble-up-a-trailing-newline-char
        return ''.join(chunks).rstrip('\n')
Example #5
0
def ReadLineFromStdin():
    chars = []
    while True:
        c = posix.read(0, 1)
        if not c:
            break
        chars.append(c)

        if c == '\n':
            break
    return ''.join(chars)
Example #6
0
 def _ReadN(self, stdin_fd, n):
     # type: (int, int) -> str
     chunks = []  # type: List[str]
     bytes_left = n
     while bytes_left > 0:
         chunk = posix.read(stdin_fd, n)  # read at up to N chars
         if len(chunk) == 0:
             break
         chunks.append(chunk)
         bytes_left -= len(chunk)
     s = ''.join(chunks)
     return s
Example #7
0
def _ReadAll():
    # type: () -> str
    """Read all of stdin."""
    chunks = []  # type: List[str]
    while True:
        c = posix.read(0, 4096)
        if len(c) == 0:
            break

        chunks.append(c)

    return ''.join(chunks)
Example #8
0
def _ReadLine():
    # type: () -> str
    """Read a line from stdin."""
    # TODO: This should be an array of integers in C++
    chars = []  # type: List[str]
    while True:
        c = posix.read(0, 1)
        if len(c) == 0:
            break

        chars.append(c)

        if c == '\n':
            break

    return ''.join(chars)
Example #9
0
def _ReadLine():
    # type: () -> str
    """Read a line from stdin.

  TODO: use a more efficient function in C
  """
    chars = []
    while True:
        c = posix.read(0, 1)
        if not c:
            break

        chars.append(c)

        if c == '\n':
            break

    return ''.join(chars)
Example #10
0
  def RunCommandSub(self, node):
    p = self._MakeProcess(node,
                          disable_errexit=not self.exec_opts.strict_errexit)

    r, w = posix.pipe()
    p.AddStateChange(process.StdoutToPipe(r, w))
    pid = p.Start()
    #log('Command sub started %d', pid)

    chunks = []
    posix.close(w)  # not going to write
    while True:
      byte_str = posix.read(r, 4096)
      if not byte_str:
        break
      chunks.append(byte_str)
    posix.close(r)

    status = p.Wait(self.waiter)

    # OSH has the concept of aborting in the middle of a WORD.  We're not
    # waiting until the command is over!
    if self.exec_opts.strict_errexit:
      if self.exec_opts.ErrExit() and status != 0:
        raise util.ErrExitFailure(
            'Command sub exited with status %d (%r)', status,
            node.__class__.__name__)
    else:
      # Set a flag so we check errexit at the same time as bash.  Example:
      #
      # a=$(false)
      # echo foo  # no matter what comes here, the flag is reset
      #
      # Set ONLY until this command node has finished executing.
      self.check_command_sub_status = True
      self.mem.SetLastStatus(status)

    # Runtime errors test case: # $("echo foo > $@")
    # Why rstrip()?
    # https://unix.stackexchange.com/questions/17747/why-does-shell-command-substitution-gobble-up-a-trailing-newline-char
    return ''.join(chunks).rstrip('\n')
def ReadLineFromStdin(delim_char):
    # type: (Optional[str]) -> Tuple[str, bool]
    """Read a portion of stdin.
  
  If delim_char is set, read until that delimiter, but don't include it.
  If not set, read a line, and include the newline.
  """
    eof = False
    chars = []
    while True:
        c = posix.read(0, 1)
        if not c:
            eof = True
            break

        if c == delim_char:
            break

        chars.append(c)

    return ''.join(chars), eof
Example #12
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        arg, i = READ_SPEC.ParseCmdVal(cmd_val)

        names = cmd_val.argv[i:]
        if arg.n is not None:  # read a certain number of bytes
            stdin = sys.stdin.fileno()
            try:
                name = names[0]
            except IndexError:
                name = 'REPLY'  # default variable name
            s = ""
            if sys.stdin.isatty():  # set stdin to read in unbuffered mode
                orig_attrs = termios.tcgetattr(stdin)
                attrs = termios.tcgetattr(stdin)
                # disable canonical (buffered) mode
                # see `man termios` for an extended discussion
                attrs[3] &= ~termios.ICANON
                try:
                    termios.tcsetattr(stdin, termios.TCSANOW, attrs)
                    # posix.read always returns a single character in unbuffered mode
                    while arg.n > 0:
                        s += posix.read(stdin, 1)
                        arg.n -= 1
                finally:
                    termios.tcsetattr(stdin, termios.TCSANOW, orig_attrs)
            else:
                s_len = 0
                while arg.n > 0:
                    buf = posix.read(stdin, arg.n)
                    # EOF
                    if buf == '':
                        break
                    arg.n -= len(buf)
                    s += buf

            state.SetLocalString(self.mem, name, s)
            # NOTE: Even if we don't get n bytes back, there is no error?
            return 0

        if not names:
            names.append('REPLY')

        # leftover words assigned to the last name
        if arg.a:
            max_results = 0  # no max
        else:
            max_results = len(names)

        # We have to read more than one line if there is a line continuation (and
        # it's not -r).

        parts = []
        join_next = False
        while True:
            line = ReadLineFromStdin()
            #log('LINE %r', line)
            if not line:  # EOF
                status = 1
                break

            if line.endswith('\n'):  # strip trailing newline
                line = line[:-1]
                status = 0
            else:
                # odd bash behavior: fail even if we can set variables.
                status = 1

            spans = self.splitter.SplitForRead(line, not arg.r)
            done, join_next = _AppendParts(line, spans, max_results, join_next,
                                           parts)

            #log('PARTS %s continued %s', parts, continued)
            if done:
                break

        if arg.a:
            state.SetArrayDynamic(self.mem, arg.a, parts)
        else:
            for i in xrange(max_results):
                try:
                    s = parts[i]
                except IndexError:
                    s = ''  # if there are too many variables
                #log('read: %s = %s', names[i], s)
                state.SetStringDynamic(self.mem, names[i], s)

        return status
Example #13
0
 def testEmptyReadAndWrite(self):
     # Regression for bug where this would hang
     posix_.read(0, 0)
     posix_.write(1, '')
Example #14
0
    def Run(self, cmd_val):
        # type: (cmd_value__Argv) -> int
        attrs, arg_r = flag_spec.ParseCmdVal('read', cmd_val)
        arg = arg_types.read(attrs.attrs)
        names = arg_r.Rest()

        if arg.n >= 0:  # read a certain number of bytes (-1 means unset)
            if len(names):
                name = names[0]
            else:
                name = 'REPLY'  # default variable name

            status = 0
            stdin_fd = self.stdin.fileno()
            if self.stdin.isatty():  # set stdin to read in unbuffered mode
                s = passwd.ReadBytesFromTerminal(stdin_fd, arg.n)
            else:
                chunks = []  # type: List[str]
                n = arg.n
                while n > 0:
                    chunk = posix.read(stdin_fd, n)  # read at up to N chars
                    if len(chunk) == 0:
                        break
                    chunks.append(chunk)
                    n -= len(chunk)
                s = ''.join(chunks)

            # DIdn't read all the bytes we wanted
            if len(s) != n:
                status = 1

            state.SetStringDynamic(self.mem, name, s)
            # NOTE: Even if we don't get n bytes back, there is no error?
            return status

        if len(names) == 0:
            names.append('REPLY')

        # leftover words assigned to the last name
        if arg.a is not None:
            max_results = 0  # no max
        else:
            max_results = len(names)

        if arg.d is not None:
            if len(arg.d):
                delim_char = arg.d[0]
            else:
                delim_char = '\0'  # -d '' delimits by NUL
        else:
            delim_char = '\n'  # read a line

        # We have to read more than one line if there is a line continuation (and
        # it's not -r).
        parts = []  # type: List[mylib.BufWriter]
        join_next = False
        status = 0
        while True:
            line, eof = ReadLineFromStdin(delim_char)

            if eof:
                # status 1 to terminate loop.  (This is true even though we set
                # variables).
                status = 1

            #log('LINE %r', line)
            if len(line) == 0:
                break

            spans = self.splitter.SplitForRead(line, not arg.r)
            done, join_next = _AppendParts(line, spans, max_results, join_next,
                                           parts)

            #log('PARTS %s continued %s', parts, continued)
            if done:
                break

        entries = [buf.getvalue() for buf in parts]
        num_parts = len(entries)
        if arg.a is not None:
            state.SetArrayDynamic(self.mem, arg.a, entries)
        else:
            for i in xrange(max_results):
                if i < num_parts:
                    s = entries[i]
                else:
                    s = ''  # if there are too many variables
                #log('read: %s = %s', names[i], s)
                state.SetStringDynamic(self.mem, names[i], s)

        return status
Example #15
0
    def RunCommandSub(self, cs_part):
        # type: (command_sub) -> str

        if not self.exec_opts.allow_command_sub():
            # TODO:
            # - Add spid of $(
            # - Better hints.  Use 'run' for 'if myfunc', and 2 lines like local x;
            #   x=$(false) fo assignment builtins.
            # - Maybe we should have an error message ID that links somewhere?

            e_die(
                "Command subs not allowed here because status wouldn't be checked (strict_errexit)."
            )

        node = cs_part.child

        # Hack for weird $(<file) construct
        if node.tag_() == command_e.Simple:
            simple = cast(command__Simple, node)
            # Detect '< file'
            if (len(simple.words) == 0 and len(simple.redirects) == 1
                    and simple.redirects[0].op.id == Id.Redir_Less):
                # change it to __cat < file
                # note: cmd_eval.py _Dispatch works around lack of spid
                tok = Token(Id.Lit_Chars, runtime.NO_SPID, '__cat')
                cat_word = compound_word([tok])
                # MUTATE the command.Simple node.  This will only be done the first
                # time in the parent process.
                simple.words.append(cat_word)

        p = self._MakeProcess(node,
                              inherit_errexit=self.exec_opts.inherit_errexit())

        r, w = posix.pipe()
        p.AddStateChange(process.StdoutToPipe(r, w))
        _ = p.Start()
        #log('Command sub started %d', pid)

        chunks = []  # type: List[str]
        posix.close(w)  # not going to write
        while True:
            byte_str = posix.read(r, 4096)
            if len(byte_str) == 0:
                break
            chunks.append(byte_str)
        posix.close(r)

        status = p.Wait(self.waiter)

        # OSH has the concept of aborting in the middle of a WORD.  We're not
        # waiting until the command is over!
        if self.exec_opts.command_sub_errexit():
            if status != 0:
                raise error.ErrExit('Command sub exited with status %d (%s)' %
                                    (status, ui.CommandType(node)),
                                    span_id=cs_part.left_token.span_id,
                                    status=status)

        else:
            # Set a flag so we check errexit at the same time as bash.  Example:
            #
            # a=$(false)
            # echo foo  # no matter what comes here, the flag is reset
            #
            # Set ONLY until this command node has finished executing.

            # HACK: move this
            self.cmd_ev.check_command_sub_status = True
            self.mem.SetLastStatus(status)

        # Runtime errors test case: # $("echo foo > $@")
        # Why rstrip()?
        # https://unix.stackexchange.com/questions/17747/why-does-shell-command-substitution-gobble-up-a-trailing-newline-char
        return ''.join(chunks).rstrip('\n')