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 running chan struct{} buff bytes.Buffer log io.Writer listeners []io.Writer } func (oni *Oni) Init(cui *ConfigUI) error { var err error oni.parent = cui oni.running = make(chan struct{}, 1) oni.listeners = make([]io.Writer, 0) oni.StateChange(READY) if cui.LogPath != "" { logpath := filepath.Join(filepath.Dir(cui.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 { return err } } return err } 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 } 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.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.Kill() } func (oni *Oni) end(v interface{}) { if v == nil { oni.StateChange(ENDED) } else { oni.Error = fmt.Sprint(v) oni.StateChange(ERROR) } 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 // } // } }