package configui import ( "errors" "os" "os/exec" "runtime" "sync" "time" ) type File struct { Path string `json:"path"` Name string `json:"name"` Action string `json:"action"` // RO is readonly RO bool `json:"ro"` Lang string `json:"lang"` // Order order of the display on ui Order int `json:"order"` // used for parsing post data Data string `json:"data,omitempty"` lock sync.RWMutex `json:"-"` owner *ConfigUI `json:"-"` } func (f *File) Read() ([]byte, error) { f.lock.RLock() defer f.lock.RUnlock() data, err := os.ReadFile(f.Path) if err != nil { f.owner.log.Error(err) return nil, err } return data, nil } func (f *File) Write(data []byte) error { if f.RO { return errors.New("this file has readonly set") } f.lock.Lock() defer f.lock.Unlock() info, err := os.Stat(f.Path) if err != nil { return err } return os.WriteFile(f.Path, data, info.Mode()) } func (f *File) Do(CmdTimeout time.Duration) (string, error) { if f.Action == "" { return "", nil } f.lock.RLock() defer f.lock.RUnlock() cmd := &exec.Cmd{} if runtime.GOOS == "windows" { cmd = exec.Command(WIN_SHELL, "/c", f.Action) } else { cmd = exec.Command(UNIX_SHELL, "-c", f.Action) } f.owner.log.Info("DO: ", f.Action) done := make(chan string, 1) go func() { out, _ := cmd.CombinedOutput() // real cmd err is unhandled, but passed to client // if err != nil { // return string(out), err // } done <- string(out) }() select { case <-time.After(CmdTimeout): cmd.Process.Kill() f.owner.log.Error("timeout") return "", errors.New("command timeout") case out := <-done: f.owner.log.Info("\n", out) return out, nil } }