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)
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
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')
def ReadLineFromStdin(): chars = [] while True: c = posix.read(0, 1) if not c: break chars.append(c) if c == '\n': break return ''.join(chars)
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
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)
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)
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)
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
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
def testEmptyReadAndWrite(self): # Regression for bug where this would hang posix_.read(0, 0) posix_.write(1, '')
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
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')