func runWithPty()

in packages/pty_linux.go [45:138]


func runWithPty(cmd *exec.Cmd) ([]byte, []byte, error) {
	// Much of this logic was taken from, without the CGO stuff:
	// https://golang.org/src/os/signal/signal_cgo_test.go

	pty, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
	if err != nil {
		return nil, nil, err
	}
	defer pty.Close()

	// grantpt doesn't appear to be required anymore.

	// unlockpt
	var i int
	if err := ioctl(pty.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&i))); err != nil {
		return nil, nil, fmt.Errorf("error from ioctl TIOCSPTLCK: %v", err)
	}

	// ptsname
	var u uint32
	if err := ioctl(pty.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&u))); err != nil {
		return nil, nil, fmt.Errorf("error from ioctl TIOCGPTN: %v", err)
	}
	path := filepath.Join("/dev/pts", strconv.Itoa(int(u)))

	tty, err := os.OpenFile(path, os.O_RDWR|syscall.O_NOCTTY, 0)
	if err != nil {
		return nil, nil, err
	}
	defer tty.Close()

	if err := unix.IoctlSetWinsize(int(pty.Fd()), syscall.TIOCSWINSZ, &unix.Winsize{Row: 1, Col: 500}); err != nil {
		return nil, nil, fmt.Errorf("error from IoctlSetWinsize: %v", err)
	}

	var stderr bytes.Buffer
	cmd.Stdin = tty
	cmd.Stdout = tty
	cmd.Stderr = &stderr
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setctty: true,
		Setsid:  true,
		Ctty:    0, // Stdin of the child process
	}

	var stdout bytes.Buffer
	var retErr error
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		input := bufio.NewReader(pty)
		for {
			b, err := input.ReadBytes('\n')
			if err != nil {
				if perr, ok := err.(*os.PathError); ok {
					err = perr.Err
				}
				if err != io.EOF && err != syscall.EIO {
					retErr = err
				}
				return
			}

			if _, err := stdout.Write(b); err != nil {
				retErr = err
				return
			}
		}
	}()

	cmdErr := cmd.Run()

	if err := tty.Close(); err != nil {
		return stdout.Bytes(), stderr.Bytes(), err

	}
	wg.Wait()

	if cmdErr != nil {
		// Yum returns non-zero exit values on non-error conditions.
		// Errors which are *not* in this category indicate failure.
		if _, ok := cmdErr.(*exec.ExitError); !ok {
			return stdout.Bytes(), stderr.Bytes(), cmdErr

		}
	}

	// Exit code 0 means no updates, 1 probably means there are but we just didn't install them.
	if cmdErr == nil {
		return nil, nil, nil
	}
	return stdout.Bytes(), stderr.Bytes(), retErr
}