package configui import ( "bufio" "bytes" "fmt" "io" "os" "os/exec" "path/filepath" ) 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:"name"` 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 start chan struct{} buff bytes.Buffer log io.Writer listeners []io.Writer } func NewOni(name string, cui *ConfigUI) *Oni { var err error oni := &Oni{ Policy: NO, State: READY, parent: cui, start: make(chan struct{}, 1), listeners: make([]io.Writer, 0), } if cui.LogPath != "" { logpath := filepath.Join(filepath.Dir(cui.LogPath), name+".log") oni.LogFile = logpath oni.log, err = os.OpenFile(logpath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { panic(err) } } return oni } func (oni *Oni) Start() error { if oni.Cmd == "" { return ErrorOniNotValid } select { case oni.start <- struct{}{}: defer func() { err := recover() if err != nil { oni.end(err) } }() default: return ErrorOniHasStarted } cmd := exec.Command(oni.Cmd, oni.Args...) 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.State = STARTED go func() { oni.end(cmd.Wait()) }() return nil } func (oni *Oni) Stop() error { if oni.cmd == nil { return nil } return oni.cmd.Process.Kill() } func (oni *Oni) end(v interface{}) { if v == nil { oni.State = ENDED } else { oni.Error = fmt.Sprint(v) oni.State = ERROR } if len(oni.start) == 1 { <-oni.start } } 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())) } }() }