|
|
|
package staros
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
//StarCmd Is Here
|
|
|
|
|
|
|
|
type StarCmd struct {
|
|
|
|
CMD *exec.Cmd
|
|
|
|
outfile io.ReadCloser
|
|
|
|
infile io.WriteCloser
|
|
|
|
errfile io.ReadCloser
|
|
|
|
running int32
|
|
|
|
//Store AlL of the Standed Outputs
|
|
|
|
stdout []byte
|
|
|
|
//Store All of the Standed Errors
|
|
|
|
errout []byte
|
|
|
|
runerr error
|
|
|
|
exitcode int
|
|
|
|
stdoutBuf *bytes.Buffer
|
|
|
|
stderrBuf *bytes.Buffer
|
|
|
|
stdoutpoint int
|
|
|
|
stderrpoint int
|
|
|
|
lock sync.Mutex
|
|
|
|
prewrite []string
|
|
|
|
prewritetime time.Duration
|
|
|
|
stopctxfunc context.CancelFunc
|
|
|
|
stopctx context.Context
|
|
|
|
}
|
|
|
|
|
|
|
|
func Command(command string, args ...string) (*StarCmd, error) {
|
|
|
|
var err error
|
|
|
|
shell := new(StarCmd)
|
|
|
|
shell.running = 0
|
|
|
|
shell.prewritetime = time.Millisecond * 200
|
|
|
|
shell.stdoutBuf = bytes.NewBuffer(make([]byte, 0))
|
|
|
|
shell.stderrBuf = bytes.NewBuffer(make([]byte, 0))
|
|
|
|
shell.stopctx, shell.stopctxfunc = context.WithCancel(context.Background())
|
|
|
|
cmd := exec.Command(command, args...)
|
|
|
|
shell.CMD = cmd
|
|
|
|
shell.infile, err = shell.CMD.StdinPipe()
|
|
|
|
if err != nil {
|
|
|
|
return shell, err
|
|
|
|
}
|
|
|
|
shell.errfile, err = shell.CMD.StderrPipe()
|
|
|
|
if err != nil {
|
|
|
|
return shell, err
|
|
|
|
}
|
|
|
|
shell.outfile, err = shell.CMD.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return shell, err
|
|
|
|
}
|
|
|
|
shell.runerr = nil
|
|
|
|
shell.exitcode = -999
|
|
|
|
return shell, nil
|
|
|
|
}
|
|
|
|
func CommandContext(ctx context.Context, command string, args ...string) (*StarCmd, error) {
|
|
|
|
var err error
|
|
|
|
shell := new(StarCmd)
|
|
|
|
shell.running = 0
|
|
|
|
shell.stdoutBuf = bytes.NewBuffer(make([]byte, 0))
|
|
|
|
shell.stderrBuf = bytes.NewBuffer(make([]byte, 0))
|
|
|
|
shell.prewritetime = time.Millisecond * 200
|
|
|
|
shell.stopctx, shell.stopctxfunc = context.WithCancel(context.Background())
|
|
|
|
cmd := exec.CommandContext(ctx, command, args...)
|
|
|
|
shell.CMD = cmd
|
|
|
|
shell.infile, err = shell.CMD.StdinPipe()
|
|
|
|
if err != nil {
|
|
|
|
return shell, err
|
|
|
|
}
|
|
|
|
shell.errfile, err = shell.CMD.StderrPipe()
|
|
|
|
if err != nil {
|
|
|
|
return shell, err
|
|
|
|
}
|
|
|
|
shell.outfile, err = shell.CMD.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return shell, err
|
|
|
|
}
|
|
|
|
shell.runerr = nil
|
|
|
|
shell.exitcode = -999
|
|
|
|
return shell, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) queryStdout(ctx context.Context) {
|
|
|
|
for starcli.IsRunning() && starcli.CMD != nil {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
out := make([]byte, 65535)
|
|
|
|
n, err := starcli.outfile.Read(out)
|
|
|
|
if n != 0 {
|
|
|
|
starcli.lock.Lock()
|
|
|
|
starcli.stdoutBuf.Write(out[:n])
|
|
|
|
starcli.lock.Unlock()
|
|
|
|
for _, v := range out[:n] {
|
|
|
|
starcli.stdout = append(starcli.stdout, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
if !strings.Contains(err.Error(), "file already closed") {
|
|
|
|
starcli.runerr = err
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) queryStderr(ctx context.Context) {
|
|
|
|
for starcli.IsRunning() && starcli.CMD != nil {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
out := make([]byte, 65535)
|
|
|
|
n, err := starcli.errfile.Read(out)
|
|
|
|
if n != 0 {
|
|
|
|
starcli.lock.Lock()
|
|
|
|
starcli.stderrBuf.Write(out[:n])
|
|
|
|
starcli.lock.Unlock()
|
|
|
|
for _, v := range out[:n] {
|
|
|
|
starcli.errout = append(starcli.errout, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
if !strings.Contains(err.Error(), "file already closed") {
|
|
|
|
starcli.runerr = err
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
func (starcli *StarCmd) NowLineOutput() (string, error) {
|
|
|
|
starcli.lock.Lock()
|
|
|
|
buf, _ := starcli.stdoutBuf.ReadBytes('\n')
|
|
|
|
buferr, _ := starcli.stderrBuf.ReadBytes(byte('\n'))
|
|
|
|
starcli.lock.Unlock()
|
|
|
|
if len(buferr) != 0 {
|
|
|
|
return string(buf), errors.New(string(buferr))
|
|
|
|
}
|
|
|
|
return string(buf), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) NowLineStdOut() string {
|
|
|
|
starcli.lock.Lock()
|
|
|
|
defer starcli.lock.Unlock()
|
|
|
|
buf, _ := starcli.stdoutBuf.ReadBytes('\n')
|
|
|
|
return string(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) NowLineStdErr() error {
|
|
|
|
starcli.lock.Lock()
|
|
|
|
defer starcli.lock.Unlock()
|
|
|
|
buferr, _ := starcli.stderrBuf.ReadBytes(byte('\n'))
|
|
|
|
if len(buferr) != 0 {
|
|
|
|
return errors.New(string(buferr))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) NowAllOutput() (string, error) {
|
|
|
|
var outstr string
|
|
|
|
starcli.lock.Lock()
|
|
|
|
buf := make([]byte, starcli.stdoutBuf.Len())
|
|
|
|
n, _ := starcli.stdoutBuf.Read(buf)
|
|
|
|
starcli.lock.Unlock()
|
|
|
|
if n != 0 {
|
|
|
|
outstr = string(buf[:n])
|
|
|
|
}
|
|
|
|
if starcli.runerr != nil {
|
|
|
|
return outstr, starcli.runerr
|
|
|
|
}
|
|
|
|
starcli.lock.Lock()
|
|
|
|
buf = make([]byte, starcli.stderrBuf.Len())
|
|
|
|
n, _ = starcli.stderrBuf.Read(buf)
|
|
|
|
starcli.lock.Unlock()
|
|
|
|
if n != 0 {
|
|
|
|
return outstr, errors.New(string(buf[:n]))
|
|
|
|
}
|
|
|
|
return outstr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) NowStdOut() string {
|
|
|
|
var outstr string
|
|
|
|
starcli.lock.Lock()
|
|
|
|
buf := make([]byte, starcli.stdoutBuf.Len())
|
|
|
|
n, _ := starcli.stdoutBuf.Read(buf)
|
|
|
|
starcli.lock.Unlock()
|
|
|
|
if n != 0 {
|
|
|
|
outstr = string(buf[:n])
|
|
|
|
}
|
|
|
|
return outstr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) NowStdErr() error {
|
|
|
|
starcli.lock.Lock()
|
|
|
|
buf := make([]byte, starcli.stderrBuf.Len())
|
|
|
|
n, _ := starcli.stderrBuf.Read(buf)
|
|
|
|
starcli.lock.Unlock()
|
|
|
|
if n != 0 {
|
|
|
|
return errors.New(string(buf[:n]))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) AllOutPut() (string, error) {
|
|
|
|
err := starcli.runerr
|
|
|
|
if err == nil && len(starcli.errout) != 0 {
|
|
|
|
err = errors.New(string(starcli.errout))
|
|
|
|
}
|
|
|
|
return string(starcli.stdout), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) AllStdOut() string {
|
|
|
|
return string(starcli.stdout)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) AllStdErr() error {
|
|
|
|
err := starcli.runerr
|
|
|
|
if err == nil && len(starcli.errout) != 0 {
|
|
|
|
err = errors.New(string(starcli.errout))
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) setRunning(alive bool) {
|
|
|
|
if alive {
|
|
|
|
val := atomic.LoadInt32(&starcli.running)
|
|
|
|
if val == 0 {
|
|
|
|
atomic.AddInt32(&starcli.running, 1)
|
|
|
|
} else {
|
|
|
|
atomic.AddInt32(&starcli.running, 1-val)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
val := atomic.LoadInt32(&starcli.running)
|
|
|
|
if val == 1 {
|
|
|
|
atomic.AddInt32(&starcli.running, -1)
|
|
|
|
} else {
|
|
|
|
atomic.AddInt32(&starcli.running, -val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func (starcli *StarCmd) Start() error {
|
|
|
|
if err := starcli.CMD.Start(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
starcli.setRunning(true)
|
|
|
|
go func() {
|
|
|
|
err := starcli.CMD.Wait()
|
|
|
|
if err != nil {
|
|
|
|
starcli.runerr = err
|
|
|
|
}
|
|
|
|
starcli.stopctxfunc()
|
|
|
|
starcli.setRunning(false)
|
|
|
|
if starcli.CMD.ProcessState != nil {
|
|
|
|
starcli.exitcode = starcli.CMD.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
go starcli.queryStdout(starcli.stopctx)
|
|
|
|
go starcli.queryStderr(starcli.stopctx)
|
|
|
|
go func(ctx context.Context) {
|
|
|
|
if len(starcli.prewrite) != 0 {
|
|
|
|
for _, v := range starcli.prewrite {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
starcli.WriteCmd(v)
|
|
|
|
time.Sleep(starcli.prewritetime)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(starcli.stopctx)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) IsRunning() bool {
|
|
|
|
return 0 != atomic.LoadInt32(&starcli.running)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) Stoped() <-chan struct{} {
|
|
|
|
return starcli.stopctx.Done()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) Exec(cmd string, wait int) (string, error) {
|
|
|
|
starcli.infile.Write([]byte(cmd + "\n"))
|
|
|
|
time.Sleep(time.Millisecond * time.Duration(wait))
|
|
|
|
return starcli.NowAllOutput()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) WriteCmd(cmdstr string) {
|
|
|
|
starcli.infile.Write([]byte(cmdstr + "\n"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) PreWrite(cmd ...string) {
|
|
|
|
for _, v := range cmd {
|
|
|
|
starcli.prewrite = append(starcli.prewrite, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) PreWriteInterval(dt time.Duration) {
|
|
|
|
starcli.prewritetime = dt
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) ExitCode() int {
|
|
|
|
return starcli.exitcode
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) Kill() error {
|
|
|
|
err := starcli.CMD.Process.Kill()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
starcli.setRunning(false)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) GetPid() int {
|
|
|
|
return starcli.CMD.Process.Pid
|
|
|
|
}
|
|
|
|
|
|
|
|
func (starcli *StarCmd) Signal(sig os.Signal) error {
|
|
|
|
return starcli.CMD.Process.Signal(sig)
|
|
|
|
}
|