configui/muzan.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
}