2022-10-04 15:25:58 +00:00
|
|
|
package reveil
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"crypto/sha512"
|
|
|
|
"errors"
|
2022-12-08 16:49:15 +00:00
|
|
|
"fmt"
|
2022-10-04 15:25:58 +00:00
|
|
|
"io/fs"
|
|
|
|
"log"
|
|
|
|
"os"
|
2022-12-08 16:49:15 +00:00
|
|
|
"os/exec"
|
2022-10-04 15:25:58 +00:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"git.nemunai.re/nemunaire/reveil/config"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Action struct {
|
|
|
|
Id Identifier `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Description string `json:"description,omitempty"`
|
|
|
|
Path string `json:"path"`
|
|
|
|
Enabled bool `json:"enabled"`
|
2022-12-08 16:49:15 +00:00
|
|
|
fullPath string
|
2022-10-04 15:25:58 +00:00
|
|
|
}
|
|
|
|
|
2022-12-08 16:49:15 +00:00
|
|
|
func loadAction(path string) (string, string, error) {
|
2022-10-04 15:25:58 +00:00
|
|
|
fd, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
defer fd.Close()
|
|
|
|
|
|
|
|
fileScanner := bufio.NewScanner(fd)
|
|
|
|
fileScanner.Split(bufio.ScanLines)
|
|
|
|
|
|
|
|
var (
|
|
|
|
shebang = false
|
|
|
|
name = ""
|
|
|
|
description = ""
|
|
|
|
)
|
|
|
|
for fileScanner.Scan() {
|
|
|
|
line := strings.TrimSpace(fileScanner.Text())
|
|
|
|
if !shebang {
|
|
|
|
if len(line) < 2 || line[0] != '#' || line[1] != '!' {
|
|
|
|
return name, description, errors.New("Not a valid action file (shebang not found).")
|
|
|
|
}
|
|
|
|
shebang = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(line) < 2 || line[0] != '#' {
|
|
|
|
if len(description) > 0 {
|
|
|
|
return name, strings.TrimSpace(description), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(name) == 0 {
|
|
|
|
name = strings.TrimSpace(line[1:])
|
|
|
|
} else {
|
|
|
|
description += strings.TrimSpace(line[1:]) + "\n"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return name, description, nil
|
|
|
|
}
|
|
|
|
|
2022-12-08 16:49:15 +00:00
|
|
|
func LoadAction(cfg *config.Config, path string) (*Action, error) {
|
|
|
|
actionsDir, err := filepath.Abs(cfg.ActionsDir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
path = filepath.Join(actionsDir, path)
|
|
|
|
|
|
|
|
d, err := os.Stat(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !d.Mode().IsRegular() {
|
|
|
|
return nil, fmt.Errorf("%q is not a file, it cannot be an action.", path)
|
|
|
|
}
|
|
|
|
|
|
|
|
hash := sha512.Sum512([]byte(path))
|
|
|
|
|
|
|
|
// Parse content
|
|
|
|
name, description, err := loadAction(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Invalid action file (trying to parse %s): %s", path, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if apath, err := filepath.Abs(path); err == nil {
|
|
|
|
path = apath
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Action{
|
|
|
|
Id: hash[:],
|
|
|
|
Name: name,
|
|
|
|
Description: description,
|
|
|
|
Path: strings.TrimPrefix(path, actionsDir+"/"),
|
|
|
|
Enabled: d.Mode().Perm()&0111 != 0,
|
|
|
|
fullPath: path,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:25:58 +00:00
|
|
|
func LoadActions(cfg *config.Config) (actions []*Action, err error) {
|
2022-10-04 17:04:57 +00:00
|
|
|
actionsDir, err := filepath.Abs(cfg.ActionsDir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:25:58 +00:00
|
|
|
err = filepath.Walk(cfg.ActionsDir, func(path string, d fs.FileInfo, err error) error {
|
|
|
|
if d.Mode().IsRegular() {
|
|
|
|
hash := sha512.Sum512([]byte(path))
|
|
|
|
|
|
|
|
// Parse content
|
2022-12-08 16:49:15 +00:00
|
|
|
name, description, err := loadAction(path)
|
2022-10-04 15:25:58 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Invalid action file (trying to parse %s): %s", path, err.Error())
|
|
|
|
// Ignore invalid files
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if description == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-04 17:04:57 +00:00
|
|
|
if apath, err := filepath.Abs(path); err == nil {
|
|
|
|
path = apath
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:25:58 +00:00
|
|
|
actions = append(actions, &Action{
|
|
|
|
Id: hash[:],
|
|
|
|
Name: name,
|
|
|
|
Description: description,
|
2022-10-04 17:04:57 +00:00
|
|
|
Path: strings.TrimPrefix(path, actionsDir+"/"),
|
2022-10-04 15:25:58 +00:00
|
|
|
Enabled: d.Mode().Perm()&0111 != 0,
|
2022-12-08 16:49:15 +00:00
|
|
|
fullPath: path,
|
2022-10-04 15:25:58 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Action) Rename(name string) error {
|
|
|
|
return errors.New("Not implemeted")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Action) Enable() error {
|
|
|
|
fi, err := os.Stat(a.Path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return os.Chmod(a.Path, fi.Mode().Perm()|0111)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Action) Disable() error {
|
|
|
|
fi, err := os.Stat(a.Path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return os.Chmod(a.Path, fi.Mode().Perm()&0666)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Action) Remove() error {
|
|
|
|
return os.Remove(a.Path)
|
|
|
|
}
|
2022-12-08 16:49:15 +00:00
|
|
|
|
2023-11-04 09:04:29 +00:00
|
|
|
func (a *Action) Launch(settings *Settings) (cmd *exec.Cmd, err error) {
|
2022-12-08 16:49:15 +00:00
|
|
|
cmd = exec.Command(a.fullPath)
|
2023-11-04 09:04:29 +00:00
|
|
|
cmd.Env = append(cmd.Environ(), fmt.Sprintf("LANG=%s", settings.Language))
|
2022-12-08 16:49:15 +00:00
|
|
|
err = cmd.Start()
|
|
|
|
return
|
|
|
|
}
|