Exec command into a different opened tty issues

Does anybody have any idea why when running an exec.Command into a different tty such as /dev/ttys001 opened in a separate terminal tab, doesn’t properly register std input whilst the command is running?

For example, the following code works fine and loads vim in the /dev/ttys001's terminal tab, but when actually switching to /dev/ttys001's tab from the tab that started the exec.Command, stdin is choppy and doesn’t work correctly.

Is this because trying to write to the tab attempts to write to vim and the tabs stdin at the same time?

package main

import (
	"log"
	"os"
	"os/exec"
)

func main() {
	tty, err := os.OpenFile("/dev/ttys001", os.O_RDWR, os.ModePerm)
	if err != nil {
		log.Fatalln(err)
	}
    defer tty.Close()

	c := exec.Command("vim")
	c.Stdin = tty
	c.Stdout = tty
	c.Stderr = tty

	if err := c.Run(); err != nil {
		log.Fatalln(err)
	}
}

I have also tried setting some of the c.SysProcAttr attributes, but can’t seem to get it working correctly in any scenario.

Hey guys,

For anybody who’s interested, I came up with a solution, but I ended up having to use system calls instead of the os/exec package’s functions.

Here’s an example of my solution:

Edit: Went slightly over the top with system calls before when I wrote down that first solution, so this edit is just posting a cleaner solution.

package main

import (
	"log"
	"os"
	"syscall"
	"unsafe"
)

var tty = "/dev/ttys001"
var cmd = "vim\n"

func main() {
	ttyFile, err := os.Open(tty)
	if err != nil {
		log.Fatalln(err)
	}
	defer ttyFile.Close()

	cbs, err := syscall.ByteSliceFromString(cmd)
	if err != nil {
		log.Fatalln(err)
	}

	var eno syscall.Errno
	for _, c := range cbs {
		_, _, eno = syscall.Syscall(syscall.SYS_IOCTL,
			ttyFile.Fd(),
			syscall.TIOCSTI,
			uintptr(unsafe.Pointer(&c)),
		)
		if eno != 0 {
			log.Fatalln(eno)
		}
	}
}
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.