Skip to content

Commit

Permalink
cmd/utils: Add an asynchronous cancellable way to discard input
Browse files Browse the repository at this point in the history
A subsequent commit will use this to ensure that the user can still
interact with the image download prompt while 'skopeo inspect' fetches
the image size from the remote registry.

Initially, the prompt will be shown without the image size.  Once the
size has been fetched, the older prompt will be cancelled and a new one
will be shown that includes the size.  While the prompt is getting
updated, the terminal device will be put into non-canonical mode input
and the echoing of input characters will be disabled to retain full
control of the cursor position.  Once the new prompt is in place, the
previous state of the terminal will be restored.  However, anything that
was typed in the interim will be discarded to avoid surprising the user
with invisible input.

#752
#1263
  • Loading branch information
debarshiray committed Dec 12, 2023
1 parent 2f25ba4 commit 2149609
Showing 1 changed file with 87 additions and 0 deletions.
87 changes: 87 additions & 0 deletions src/cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,93 @@ func askForConfirmationAsync(ctx context.Context,
return retValCh, errCh
}

func discardInputAsync(ctx context.Context) (<-chan int, <-chan error) {
retValCh := make(chan int, 1)
errCh := make(chan error, 1)

done := ctx.Done()
eventFD := -1
if done != nil {
fd, err := unix.Eventfd(0, unix.EFD_CLOEXEC|unix.EFD_NONBLOCK)
if err != nil {
errCh <- fmt.Errorf("eventfd(2) failed: %w", err)
return retValCh, errCh
}

eventFD = fd
}

go func() {
var total int

for {
pollFn := func(errPoll error, pollFDs []unix.PollFd) error {
if len(pollFDs) != 1 {
panic("unexpected number of file descriptors")
}

if errPoll != nil && !errors.Is(errPoll, context.Canceled) {
return errPoll
}

if pollFDs[0].Revents&unix.POLLIN != 0 {
logrus.Debug("Returned from /dev/stdin: POLLIN")

buffer := make([]byte, 1)
n, err := os.Stdin.Read(buffer)
total += n

if errPoll != nil {
return errPoll
} else if err != nil {
return err
}

return nil
}

if pollFDs[0].Revents&unix.POLLHUP != 0 {
logrus.Debug("Returned from /dev/stdin: POLLHUP")

if errPoll != nil {
return errPoll
}

return errHUP
}

if pollFDs[0].Revents&unix.POLLNVAL != 0 {
logrus.Debug("Returned from /dev/stdin: POLLNVAL")

if errPoll != nil {
return errPoll
}

return errClosed
}

if errPoll != nil {
return errPoll
}

return errContinue
}

stdinFD := int32(os.Stdin.Fd())

err := poll(pollFn, int32(eventFD), stdinFD)
if err != nil {
retValCh <- total
errCh <- err
break
}
}
}()

watchContextForEventFD(ctx, eventFD)
return retValCh, errCh
}

func createErrorContainerNotFound(container string) error {
var builder strings.Builder
fmt.Fprintf(&builder, "container %s not found\n", container)
Expand Down

0 comments on commit 2149609

Please sign in to comment.