diff --git a/tools/utils/tty.go b/tools/utils/tty.go index 361b23735..ceb105287 100644 --- a/tools/utils/tty.go +++ b/tools/utils/tty.go @@ -19,14 +19,36 @@ type Term struct { states []unix.Termios } +func eintr_retry_noret(f func() error) error { + for { + qerr := f() + if qerr == unix.EINTR { + continue + } + return qerr + } +} + +func eintr_retry_intret(f func() (int, error)) (int, error) { + for { + q, qerr := f() + if qerr == unix.EINTR { + continue + } + return q, qerr + } +} + func OpenTerm(name string, in_raw_mode bool) (self *Term, err error) { - fd, err := unix.Open(name, unix.O_NOCTTY|unix.O_CLOEXEC|unix.O_NDELAY|unix.O_RDWR, 0666) + fd, err := eintr_retry_intret(func() (int, error) { + return unix.Open(name, unix.O_NOCTTY|unix.O_CLOEXEC|unix.O_NDELAY|unix.O_RDWR, 0666) + }) if err != nil { return nil, &os.PathError{Op: "open", Path: name, Err: err} } self = &Term{name: name, fd: fd} - err = unix.SetNonblock(self.fd, false) + err = eintr_retry_noret(func() error { return unix.SetNonblock(self.fd, false) }) if err != nil { return } @@ -46,19 +68,27 @@ func OpenControllingTerm(in_raw_mode bool) (self *Term, err error) { func (self *Term) Fd() int { return self.fd } func (self *Term) Close() error { - err := unix.Close(self.fd) + err := eintr_retry_noret(func() error { return unix.Close(self.fd) }) self.fd = -1 return err } +func (self *Term) Tcgetattr(ans *unix.Termios) error { + return eintr_retry_noret(func() error { return termios.Tcgetattr(uintptr(self.fd), ans) }) +} + +func (self *Term) Tcsetattr(when uintptr, ans *unix.Termios) error { + return eintr_retry_noret(func() error { return termios.Tcsetattr(uintptr(self.fd), when, ans) }) +} + func (self *Term) SetRawWhen(when uintptr) (err error) { var state unix.Termios - if err = termios.Tcgetattr(uintptr(self.fd), &state); err != nil { + if err = self.Tcgetattr(&state); err != nil { return } new_state := state termios.Cfmakeraw(&new_state) - err = termios.Tcsetattr(uintptr(self.fd), when, &new_state) + err = self.Tcsetattr(when, &new_state) if err != nil { self.states = append(self.states, state) } @@ -74,7 +104,7 @@ func (self *Term) PopStateWhen(when uintptr) (err error) { return nil } idx := len(self.states) - 1 - err = termios.Tcsetattr(uintptr(self.fd), when, &self.states[idx]) + err = self.Tcsetattr(when, &self.states[idx]) if err != nil { self.states = self.states[:idx] } @@ -121,12 +151,12 @@ func get_vmin_and_vtime(d time.Duration) (uint8, uint8) { func (self *Term) SetReadTimeout(d time.Duration) (err error) { var a unix.Termios - if err := termios.Tcgetattr(uintptr(self.fd), &a); err != nil { + if err := self.Tcgetattr(&a); err != nil { return err } b := a b.Cc[unix.VMIN], b.Cc[unix.VTIME] = get_vmin_and_vtime(d) - err = termios.Tcsetattr(uintptr(self.fd), termios.TCSANOW, &b) + err = self.Tcsetattr(termios.TCSANOW, &b) if err != nil { self.states = append(self.states, a) } @@ -134,23 +164,33 @@ func (self *Term) SetReadTimeout(d time.Duration) (err error) { } func (self *Term) ReadWithTimeout(b []byte, d time.Duration) (n int, err error) { - var read, write, in_err unix.FdSet tv := NsecToTimespec(d) - read.Set(self.fd) - num_ready, err := unix.Pselect(self.fd+1, &read, &write, &in_err, &tv, nil) - if err != nil { - return + var read, write, in_err unix.FdSet + pselect := func() (int, error) { + read.Zero() + write.Zero() + in_err.Zero() + read.Set(self.fd) + return unix.Pselect(self.fd+1, &read, &write, &in_err, &tv, nil) } - if num_ready == 0 { - err = os.ErrDeadlineExceeded - return + for { + num_ready, qerr := eintr_retry_intret(pselect) + if qerr != nil { + err = qerr + return + } + if num_ready == 0 { + err = os.ErrDeadlineExceeded + return + } + break } return self.Read(b) } func (t *Term) Read(b []byte) (int, error) { - n, e := unix.Read(t.fd, b) + n, e := eintr_retry_intret(func() (int, error) { return unix.Read(t.fd, b) }) if n < 0 { n = 0 } @@ -164,7 +204,7 @@ func (t *Term) Read(b []byte) (int, error) { } func (t *Term) Write(b []byte) (int, error) { - n, e := unix.Write(t.fd, b) + n, e := eintr_retry_intret(func() (int, error) { return unix.Write(t.fd, b) }) if n < 0 { n = 0 } @@ -202,16 +242,21 @@ func (self *Term) WriteAllWithTimeout(b []byte, d time.Duration) (n int, err err var read, write, in_err unix.FdSet var num_ready int n = len(b) + sysnv := NsecToTimespec(d) + pselect := func() (int, error) { + write.Zero() + read.Zero() + in_err.Zero() + write.Set(self.fd) + return unix.Pselect(self.fd+1, &read, &write, &in_err, &sysnv, nil) + } for { if len(b) == 0 { return } - sysnv := NsecToTimespec(d) read.Zero() - write.Zero() in_err.Zero() - write.Set(self.fd) - num_ready, err = unix.Pselect(self.fd+1, &read, &write, &in_err, &sysnv, nil) + num_ready, err = pselect() if err != nil { n -= len(b) return