173 lines
3.0 KiB
Go
173 lines
3.0 KiB
Go
package configui
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"syscall"
|
|
)
|
|
|
|
type State string
|
|
|
|
const (
|
|
READY State = "ready"
|
|
ERROR State = "error"
|
|
STARTED State = "started"
|
|
ENDED State = "ended"
|
|
)
|
|
|
|
type Policy string
|
|
|
|
const (
|
|
NO Policy = "no"
|
|
ONFAIL Policy = "on-failure"
|
|
ALWAYS Policy = "always"
|
|
UNLESSSTOP Policy = "unless-stopped"
|
|
)
|
|
|
|
type Oni struct {
|
|
Name string `json:"-"`
|
|
Cmd string `json:"cmd"`
|
|
Dir string `json:"dir"`
|
|
Args []string `json:"args"`
|
|
Envs []string `json:"envs"`
|
|
Policy Policy `json:"policy"`
|
|
|
|
PID int `json:"pid"`
|
|
LogFile string `json:"log_file"`
|
|
State State `json:"state"`
|
|
Error string `json:"error"`
|
|
ManStop bool `json:"manual_stop"`
|
|
|
|
parent *ConfigUI
|
|
cmd *exec.Cmd
|
|
running chan struct{}
|
|
buff bytes.Buffer
|
|
log io.Writer
|
|
|
|
listeners []io.Writer
|
|
}
|
|
|
|
func (oni *Oni) Init(cui *ConfigUI) {
|
|
oni.parent = cui
|
|
oni.running = make(chan struct{}, 1)
|
|
oni.listeners = make([]io.Writer, 0)
|
|
oni.StateChange(READY)
|
|
oni.SetLog()
|
|
}
|
|
|
|
func (oni *Oni) Start() error {
|
|
if oni.Cmd == "" {
|
|
return ErrorOniNotValid
|
|
}
|
|
select {
|
|
case oni.running <- struct{}{}:
|
|
defer func() {
|
|
err := recover()
|
|
if err != nil {
|
|
oni.end(err)
|
|
}
|
|
}()
|
|
default:
|
|
return ErrorOniHasStarted
|
|
}
|
|
var err error
|
|
cmd := exec.Command(oni.Cmd, oni.Args...)
|
|
setgpid(cmd)
|
|
cmd.Env = oni.Envs
|
|
cmd.Dir = oni.Dir
|
|
var out io.Writer
|
|
if oni.LogFile != "" {
|
|
out = io.MultiWriter(oni.log, &oni.buff)
|
|
} else {
|
|
out = &oni.buff
|
|
}
|
|
cmd.Stderr = out
|
|
cmd.Stdout = out
|
|
oni.cmd = cmd
|
|
err = oni.cmd.Start()
|
|
if err != nil {
|
|
oni.end(err)
|
|
return err
|
|
}
|
|
go oni.read()
|
|
oni.PID = cmd.Process.Pid
|
|
oni.StateChange(STARTED)
|
|
go func() {
|
|
oni.end(cmd.Wait())
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (oni *Oni) Stop() error {
|
|
if oni.cmd == nil {
|
|
return nil
|
|
}
|
|
return oni.cmd.Process.Signal(syscall.SIGTERM)
|
|
}
|
|
|
|
func (oni *Oni) end(v interface{}) {
|
|
if v == nil {
|
|
oni.StateChange(ENDED)
|
|
} else {
|
|
oni.Error = fmt.Sprint(v)
|
|
oni.StateChange(ERROR)
|
|
}
|
|
if oni.log != nil {
|
|
file, ok := oni.log.(*os.File)
|
|
if ok {
|
|
file.Close()
|
|
}
|
|
}
|
|
if len(oni.running) == 1 {
|
|
<-oni.running
|
|
}
|
|
}
|
|
|
|
func (oni *Oni) read() {
|
|
go func() {
|
|
scanner := bufio.NewScanner(&oni.buff)
|
|
for scanner.Scan() {
|
|
if len(oni.listeners) == 0 {
|
|
continue
|
|
}
|
|
w := io.MultiWriter(oni.listeners...)
|
|
w.Write([]byte(scanner.Text()))
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (oni *Oni) StateChange(state State) {
|
|
oni.State = state
|
|
// for i := range oni.parent.Actions {
|
|
// if oni.parent.Actions[i].Name == oni.Name {
|
|
// oni.parent.Actions[i].State = state
|
|
// }
|
|
// }
|
|
}
|
|
|
|
func (oni *Oni) SetLog() error {
|
|
var err error
|
|
if oni.log != nil {
|
|
file, ok := oni.log.(*os.File)
|
|
if ok {
|
|
file.Close()
|
|
}
|
|
}
|
|
if oni.parent.LogPath != "" {
|
|
logpath := filepath.Join(filepath.Dir(oni.parent.LogPath), oni.Name+".log")
|
|
oni.LogFile = logpath
|
|
oni.log, err = os.OpenFile(logpath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
oni.end(err)
|
|
return err
|
|
}
|
|
}
|
|
return err
|
|
}
|