diff --git a/go.mod b/go.mod index 8e84165..11eb30a 100644 --- a/go.mod +++ b/go.mod @@ -9,4 +9,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) -require github.com/mattn/go-isatty v0.0.17 // indirect +require ( + github.com/creack/pty v1.1.18 + github.com/mattn/go-isatty v0.0.17 // indirect +) diff --git a/go.sum b/go.sum index cd3a846..6379bc3 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= diff --git a/main.go b/main.go index d1459b2..db50446 100644 --- a/main.go +++ b/main.go @@ -98,6 +98,9 @@ var exitOnStop = flag.Bool("exit-on-stop", true, "Exit goreman if all subprocess // show timestamp in log var logTime = flag.Bool("logtime", true, "show timestamp in log") +// use a PTY for all subprocesses +var usePty = flag.Bool("pty", false, "use a PTY for all subprocesses (noop on Windows)") + var maxProcNameLength = 0 var re = regexp.MustCompile(`\$([a-zA-Z]+[a-zA-Z0-9_]+)`) diff --git a/proc.go b/proc.go index 01b4c70..378f72e 100644 --- a/proc.go +++ b/proc.go @@ -17,10 +17,16 @@ func spawnProc(name string, errCh chan<- error) { cs := append(cmdStart, proc.cmdline) cmd := exec.Command(cs[0], cs[1:]...) cmd.Stdin = nil - cmd.Stdout = logger - cmd.Stderr = logger cmd.SysProcAttr = procAttrs + if err := startPTY(logger, cmd); err != nil { + select { + case errCh <- err: + default: + } + fmt.Fprintf(logger, "Failed to open pty for %s: %s\n", name, err) + return + } if proc.setPort { cmd.Env = append(os.Environ(), fmt.Sprintf("PORT=%d", proc.port)) fmt.Fprintf(logger, "Starting %s on port %d\n", name, proc.port) diff --git a/proc_posix.go b/proc_posix.go index 7dbd375..2311bee 100644 --- a/proc_posix.go +++ b/proc_posix.go @@ -4,9 +4,13 @@ package main import ( + "fmt" + "io" "os" + "os/exec" "os/signal" + "github.com/creack/pty" "golang.org/x/sys/unix" ) @@ -51,3 +55,21 @@ func notifyCh() <-chan os.Signal { signal.Notify(sc, sigterm, sigint, sighup) return sc } + +func startPTY(logger *clogger, cmd *exec.Cmd) error { + if *usePty { + p, t, err := pty.Open() + if err != nil { + return fmt.Errorf("failed to open PTY: %w", err) + } + defer p.Close() + defer t.Close() + cmd.Stdout = t + cmd.Stderr = t + go io.Copy(logger, p) + } else { + cmd.Stdout = logger + cmd.Stderr = logger + } + return nil +} diff --git a/proc_windows.go b/proc_windows.go index 6b0d2d6..fe3bfea 100644 --- a/proc_windows.go +++ b/proc_windows.go @@ -2,6 +2,7 @@ package main import ( "os" + "os/exec" "os/signal" "syscall" @@ -63,3 +64,9 @@ func notifyCh() <-chan os.Signal { signal.Notify(sc, os.Interrupt) return sc } + +// This is a no-op on Windows. +func startPTY(logger *clogger, cmd *exec.Cmd) error { + cmd.Stdout = logger + cmd.Stderr = logger +}