Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: initial implementation #6

Merged
merged 1 commit into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package pty

import (
"context"
"os"
"syscall"
)

// Cmd is a command that can be started attached to a pseudo-terminal.
// This is similar to the API of exec.Cmd. The main difference is that
// the command is started attached to a pseudo-terminal.
// This is required as we cannot use exec.Cmd directly on Windows due to
// limitation of starting a process attached to a pseudo-terminal.
// See: https://github.com/golang/go/issues/62708
type Cmd struct {
ctx context.Context
pty Pty
sys interface{}

// Path is the path of the command to run.
Path string

// Args holds command line arguments, including the command as Args[0].
Args []string

// Env specifies the environment of the process.
// If Env is nil, the new process uses the current process's environment.
Env []string

// Dir specifies the working directory of the command.
// If Dir is the empty string, the current directory is used.
Dir string

// SysProcAttr holds optional, operating system-specific attributes.
SysProcAttr *syscall.SysProcAttr

// Process is the underlying process, once started.
Process *os.Process

// ProcessState contains information about an exited process.
// If the process was started successfully, Wait or Run will populate this
// field when the command completes.
ProcessState *os.ProcessState

// Cancel is called when the command is canceled.
Cancel func() error
}

// Start starts the specified command attached to the pseudo-terminal.
func (c *Cmd) Start() error {
return c.start()
}

// Wait waits for the command to exit.
func (c *Cmd) Wait() error {
return c.wait()
}

// Run runs the command and waits for it to complete.
func (c *Cmd) Run() error {
if err := c.Start(); err != nil {
return err
}
return c.Wait()
}
45 changes: 45 additions & 0 deletions cmd_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//go:build !windows
// +build !windows

package pty

import (
"os/exec"

"golang.org/x/sys/unix"
)

func (c *Cmd) start() error {
cmd, ok := c.sys.(*exec.Cmd)
if !ok {
return ErrInvalidCommand
}
pty, ok := c.pty.(*PosixPty)
if !ok {
return ErrInvalidCommand
}

cmd.Stdin = pty.slave
cmd.Stdout = pty.slave
cmd.Stderr = pty.slave
cmd.SysProcAttr = &unix.SysProcAttr{
Setsid: true,
Setctty: true,
}
if err := cmd.Start(); err != nil {
return err
}

c.Process = cmd.Process
return nil
}

func (c *Cmd) wait() error {
cmd, ok := c.sys.(*exec.Cmd)
if !ok {
return ErrInvalidCommand
}
err := cmd.Wait()
c.ProcessState = cmd.ProcessState
return err
}
Loading