package configui import ( "encoding/json" "errors" "log" "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"` lock sync.RWMutex `json:"-"` } func (f *File) Read() ([]byte, error) { f.lock.RLock() defer f.lock.RUnlock() data, err := os.ReadFile(f.Path) if err != nil { log.Println(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() (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) } log.Println("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(5 * time.Second): cmd.Process.Kill() log.Println("timeout") return "", errors.New("command timeout") case out := <-done: log.Println("\n", out) return out, nil } } func ReadConfig(confstr string) ([]File, error) { conf := []File{} err := json.Unmarshal([]byte(confstr), &conf) if err != nil { return nil, err } for i := range conf { if conf[i].Name == "" { conf[i].Name = conf[i].Path } } return conf, nil } func GetFileMap(files []File) map[string]*File { fileMap := map[string]*File{} for i := range files { fileMap[files[i].Name] = &files[i] } return fileMap }