package reveil import ( "bytes" "crypto/sha512" "fmt" "io/fs" "io/ioutil" "log" "os" "path/filepath" "strconv" "strings" "time" "git.nemunai.re/nemunaire/reveil/config" ) type RoutineStep struct { Delay uint64 `json:"delay"` Action string `json:"action"` Args []string `json:"args,omitempty"` } func (s *RoutineStep) GetAction(cfg *config.Config) (*Action, error) { return LoadAction(cfg, s.Action) } type Routine struct { Id Identifier `json:"id"` Name string `json:"name"` Path string `json:"path"` Steps []RoutineStep `json:"steps"` } func LoadRoutine(path string, cfg *config.Config) ([]RoutineStep, error) { ds, err := ioutil.ReadDir(path) if err != nil { return nil, err } actionsDir, err := filepath.Abs(cfg.ActionsDir) if err != nil { return nil, err } var steps []RoutineStep for _, f := range ds { fullpath := filepath.Join(path, f.Name()) if f.Mode()&os.ModeSymlink == 0 { continue } dst, err := os.Readlink(fullpath) if err != nil { return nil, err } if adst, err := filepath.Abs(filepath.Join(path, dst)); err == nil { dst = adst } args := strings.Split(f.Name(), "_") delay, err := strconv.ParseUint(args[0], 10, 64) step := RoutineStep{ Delay: delay, Action: strings.TrimPrefix(dst, actionsDir+"/"), } if len(args) > 1 { step.Args = args[1:] } steps = append(steps, step) } return steps, nil } func LoadRoutineFromId(id Identifier, cfg *config.Config) (*Routine, error) { routines, err := LoadRoutines(cfg) if err != nil { return nil, err } for _, routine := range routines { if bytes.Equal(routine.Id, id) { return routine, nil } } return nil, fmt.Errorf("Unable to find routine %x", id) } func LoadRoutines(cfg *config.Config) (routines []*Routine, err error) { err = filepath.Walk(cfg.RoutinesDir, func(path string, d fs.FileInfo, err error) error { if d.IsDir() && path != cfg.RoutinesDir { hash := sha512.Sum512([]byte(path)) // Explore directory steps, err := LoadRoutine(path, cfg) if err != nil { log.Printf("Invalid routine directory (trying to walk through %s): %s", path, err.Error()) // Ignore invalid routines return nil } routines = append(routines, &Routine{ Id: hash[:], Name: d.Name(), Path: path, Steps: steps, }) } return nil }) return } func (r *Routine) Rename(newName string) error { newPath := filepath.Join(filepath.Dir(r.Path), newName) err := os.Rename( r.Path, newPath, ) if err != nil { return err } r.Path = newPath return nil } func (a *Routine) Remove() error { return os.Remove(a.Path) } func (a *Routine) Launch(cfg *config.Config) error { for _, s := range a.Steps { act, err := s.GetAction(cfg) if err != nil { log.Printf("Unable to get action: %s: %s", s.Action, err.Error()) continue } settings, err := ReadSettings(cfg.SettingsFile) if err != nil { log.Printf("Unable to read settings: %s", err.Error()) continue } time.Sleep(time.Duration(s.Delay) * time.Second) cmd, err := act.Launch(settings) if err != nil { log.Printf("Unable to launch the action %q: %s", s.Action, err.Error()) continue } err = cmd.Wait() if err != nil { log.Printf("Something goes wrong when waiting for the action %q's end: %s", s.Action, err.Error()) } } return nil }