Compare commits

23 Commits

Author SHA1 Message Date
e37a84d77d [triggers/ethernet] fix deprecation warning 2023-10-31 13:47:14 +01:00
2d6ca2b0bd [actions] fix missing return 2023-10-31 13:12:50 +01:00
df2b9e7624 [actions/unix_command] check if commands can be found during test run 2023-10-31 13:12:34 +01:00
46d1270648 [actions/shell_script] init 2023-10-31 13:12:34 +01:00
5cbf066ccf [actions/shutdown] add time option to control delay 2023-10-31 13:12:34 +01:00
515e244592 [docs] fix typo 2023-10-31 02:50:25 +01:00
fb4322c040 [docs] fix typos 2023-10-31 02:47:12 +01:00
f376d8684b [nix] allow configuration of triggers/actions in nix 2023-10-31 02:45:25 +01:00
d0439394cf [docs] include README.md into docs 2023-10-31 00:51:36 +01:00
a91632028c [readme] WIP update 2023-10-31 00:51:36 +01:00
bccdcf2ca3 [docs] add README for triggers and actions 2023-10-31 00:51:36 +01:00
18e3a93a38 [docs] change docbuilder markdown output 2023-10-31 00:51:36 +01:00
cfb553c975 [docs] add Examples to each trigger/action 2023-10-31 00:51:36 +01:00
1466623070 [readme] init 2023-10-31 00:51:36 +01:00
c527d40721 [docs] rm unused file 2023-10-31 00:51:36 +01:00
781c096abf [gitignore] update 2023-10-31 00:51:36 +01:00
c94fbd4b48 [gokill] mv main 2023-10-31 00:51:36 +01:00
1f00713c1e [docs] add docbuilder.go 2023-10-31 00:51:36 +01:00
12cf423550 [gitignore] update 2023-10-31 00:51:36 +01:00
f99726d3b7 [docs] setup flake and dir 2023-10-31 00:51:36 +01:00
13932e572f [docs] init 2023-10-31 00:51:36 +01:00
20120785bd [nix] add devShell 2023-10-30 20:00:43 +01:00
5af4c963ea [actions] handle errors via channel 2023-10-30 19:59:21 +01:00
11 changed files with 244 additions and 66 deletions

View File

@@ -45,7 +45,7 @@ func (a StagedActions) executeInternal(f func(Action)) {
err := <-a.ActionChan err := <-a.ActionChan
if err != nil { if err != nil {
fmt.Printf("Error occured on Stage %d: %s", idx+1, err) fmt.Printf("Error occured on Stage %d: %s\n", idx+1, err)
} }
} }
} }
@@ -122,6 +122,7 @@ func GetAllActions() []DocumentedAction {
Printer{}, Printer{},
TimeOut{}, TimeOut{},
Command{}, Command{},
ShellScript{},
Shutdown{}, Shutdown{},
} }
} }

View File

@@ -49,7 +49,7 @@ func (p Printer) GetExample() string {
return ` return `
{ {
type: "Print", type: "Print",
"options: { "options": {
"message": "Hello World!" "message": "Hello World!"
} }
} }

119
actions/shell_script.go Normal file
View File

@@ -0,0 +1,119 @@
package actions
import (
"encoding/json"
"fmt"
"os/exec"
"os"
"unknown.com/gokill/internal"
)
type ShellScript struct {
Path string `json:"path"`
ActionChan ActionResultChan
}
func isExecutableFile(path string) bool {
fi, err := os.Lstat(path)
if err != nil {
fmt.Println("Test executing Shellscript Failed.")
return false
}
mode := fi.Mode()
//TODO: should check if current user can execute
if mode&01111 == 0 {
return false
}
return true
}
func (c ShellScript) DryExecute() {
fmt.Printf("Test Executing ShellScript:\n%s\n", c.Path)
_, err := os.Open(c.Path)
if err != nil {
fmt.Println("Test executing Shellscript Failed.")
c.ActionChan <- err
return
}
if !isExecutableFile(c.Path) {
fmt.Println("Test executing Shellscript Failed.")
c.ActionChan <- fmt.Errorf("File is not executable: %s", c.Path)
return
}
c.ActionChan <- nil
}
func (c ShellScript) Execute() {
if !isExecutableFile(c.Path) {
fmt.Println("Test executing Shellscript Failed.")
c.ActionChan <- fmt.Errorf("File is not executable: %s", c.Path)
return
}
cmd := exec.Command("/bin/sh", c.Path)
stdout, err := cmd.Output()
if err != nil {
fmt.Println(err.Error())
c.ActionChan <- err
}
fmt.Println(string(stdout[:]))
c.ActionChan <- nil
}
func CreateShellScript(config internal.ActionConfig, c ActionResultChan) (ShellScript, error) {
result := ShellScript{}
err := json.Unmarshal(config.Options, &result)
if err != nil {
return ShellScript{}, err
}
if result.Path == "" {
return ShellScript{}, internal.OptionMissingError{"path"}
}
result.ActionChan = c
return result, nil
}
func (cc ShellScript) Create(config internal.ActionConfig, c ActionResultChan) (Action, error) {
return CreateShellScript(config, c)
}
func (p ShellScript) GetName() string {
return "ShellScript"
}
func (p ShellScript) GetDescription() string {
return "Executes the given shell script."
}
func (p ShellScript) GetExample() string {
return `
{
"type": "ShellScript",
"options": {
"path": "/path/to/file.sh"
}
}
`
}
func (p ShellScript) GetOptions() []internal.ConfigOption {
return []internal.ConfigOption{
{"path", "string", "path to script to execute", ""},
}
}

View File

@@ -3,23 +3,24 @@ package actions
import ( import (
"fmt" "fmt"
"os/exec" "os/exec"
"encoding/json"
"unknown.com/gokill/internal" "unknown.com/gokill/internal"
) )
type Shutdown struct { type Shutdown struct {
Timeout string `json:"time"`
ActionChan ActionResultChan ActionChan ActionResultChan
} }
func (s Shutdown) DryExecute() { func (s Shutdown) DryExecute() {
fmt.Printf("shutdown -h %s\n", s.Timeout)
fmt.Println("Test Shutdown executed...") fmt.Println("Test Shutdown executed...")
s.ActionChan <- nil s.ActionChan <- nil
} }
func (s Shutdown) Execute() { func (s Shutdown) Execute() {
if err := exec.Command("shutdown", "-h", "now").Run(); err != nil { if err := exec.Command("shutdown", "-h", s.Timeout).Run(); err != nil {
fmt.Println("Failed to initiate shutdown:", err) fmt.Println("Failed to initiate shutdown:", err)
} }
@@ -29,7 +30,16 @@ func (s Shutdown) Execute() {
} }
func (s Shutdown) Create(config internal.ActionConfig, c ActionResultChan) (Action, error) { func (s Shutdown) Create(config internal.ActionConfig, c ActionResultChan) (Action, error) {
return Shutdown{c}, nil var result Shutdown
err := json.Unmarshal(config.Options, &result)
if err != nil {
fmt.Println("Parsing Shutdown options failed.")
return Shutdown{}, err
}
result.ActionChan = c
return result, nil
} }
func (p Shutdown) GetName() string { func (p Shutdown) GetName() string {
@@ -44,10 +54,20 @@ func (p Shutdown) GetExample() string {
return ` return `
{ {
"type": "Shutdown", "type": "Shutdown",
"options": {
"time": "+5" //wait 5 minutes before shutdown
}
} }
` `
} }
func (p Shutdown) GetOptions() []internal.ConfigOption { func (p Shutdown) GetOptions() []internal.ConfigOption {
return []internal.ConfigOption{} return []internal.ConfigOption{
{
Name: "time",
Type: "string",
Description: "TIME parameter passed to shutdown as follows ```shutdown -h TIME```",
Default: "now",
},
}
} }

View File

@@ -14,8 +14,33 @@ type Command struct {
ActionChan ActionResultChan ActionChan ActionResultChan
} }
func isCommandAvailable(name string) bool {
cmd := exec.Command("/bin/sh", "-c", "command -v "+name)
if err := cmd.Run(); err != nil {
return false
}
return true
}
func (c Command) DryExecute() { func (c Command) DryExecute() {
fmt.Printf("Test Executing Command:\n%s ", c.Command) fmt.Printf("Test Executing Command:\n%s\n", c.Command)
command, _, err := c.splitCommandString()
if err != nil {
fmt.Printf("Error during argument parsing of command '%s'\n", c.Command)
fmt.Println(err)
return
}
isAvailable := isCommandAvailable(command)
if !isAvailable {
fmt.Printf("Command %s not found\n", command)
c.ActionChan <- fmt.Errorf("Command %s not found!", command)
return
}
c.ActionChan <- nil c.ActionChan <- nil
} }
@@ -48,6 +73,7 @@ func (c Command) Execute() {
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
c.ActionChan <- err
} }
fmt.Println(string(stdout[:])) fmt.Println(string(stdout[:]))

View File

@@ -15,9 +15,7 @@ Actions have the following syntax:
"type": "SomeAction", "type": "SomeAction",
"options": { //each action defines its own options "options": { //each action defines its own options
"firstOption": "someValue", "firstOption": "someValue",
"Stage": 2 //this (positive) number defines the order of multiple actions "stage": 2 //this (positive) number defines the order of multiple actions
} }
} }
``` ```
To get a list of all actions and their options from the commandline run ``` gokill -d ```

View File

@@ -17,5 +17,3 @@ Triggers have the following syntax:
"actions": [] //list actions that should be executed here "actions": [] //list actions that should be executed here
} }
``` ```
To get a list of all triggers and their options from the commandline run ```gokill -d```

112
flake.nix
View File

@@ -47,7 +47,7 @@
nixosModules.gokill = { config, lib, pkgs, ... }: nixosModules.gokill = { config, lib, pkgs, ... }:
let let
cfg = config.services.gokill; cfg = config.services.gokill;
configFile = pkgs.writeText "config.json" ''${cfg.extraConfig}''; configFile = pkgs.writeText "config.json" (builtins.toJSON cfg.triggers);
gokill-pkg = self.packages.x86_64-linux.gokill; gokill-pkg = self.packages.x86_64-linux.gokill;
in in
{ {
@@ -61,6 +61,45 @@
''; '';
}; };
triggers = lib.mkOption {
description = "list of triggers";
default = [];
type = with lib.types; lib.types.listOf ( submodule {
options = {
type = lib.mkOption {
type = lib.types.str;
};
name = lib.mkOption {
type = lib.types.str;
};
options = lib.mkOption {
type = lib.types.attrs;
};
actions = lib.mkOption {
description = "list of actions";
type = with lib.types; lib.types.listOf ( submodule {
options = {
type = lib.mkOption {
type = lib.types.str;
};
options = lib.mkOption {
type = lib.types.attrs;
};
stage = lib.mkOption {
type = lib.types.int;
};
};
});
};
};
});
};
extraConfig = lib.mkOption { extraConfig = lib.mkOption {
type = lib.types.str; type = lib.types.str;
description = lib.mdDoc '' description = lib.mdDoc ''
@@ -92,53 +131,30 @@
self.nixosModules.gokill self.nixosModules.gokill
{ {
services.gokill.enable = true; services.gokill.enable = true;
services.gokill.extraConfig = '' services.gokill.triggers = [
[ {
{ type = "Timeout";
"type": "Timeout", name = "custom timeout";
"name": "custom timeout", options = {
"options": { duration = 10;
"duration": 30 };
}, actions = [
"actions": [ {
{ type = "Timeout";
"type": "Print", options = {
"options": { duration = 5;
"message": "Stage 1 triggered. Waiting 25 seconds" };
}, stage = 1;
"stage": 1 }
}, {
{ type = "Shutdown";
"type": "Timeout", options = {
"options": { };
"duration": 20 stage = 2;
}, }
"stage": 1 ];
}, }
{ ];
"type": "Timeout",
"options": {
"duration": 5
},
"stage": 2
},
{
"type": "Print",
"options": {
"message": "Shutdown in 5 seconds..."
},
"stage": 2
},
{
"type": "Shutdown",
"options": {
},
"stage": 3
}
]
}
]
'';
users.users.root.password = "root"; users.users.root.password = "root";
virtualisation.vmVariant.virtualisation.graphics = false; virtualisation.vmVariant.virtualisation.graphics = false;
} }

View File

@@ -3,7 +3,7 @@ package triggers
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "os"
"time" "time"
"unknown.com/gokill/actions" "unknown.com/gokill/actions"
@@ -17,7 +17,7 @@ type EthernetDisconnect struct {
} }
func isEthernetConnected(deviceName string) bool { func isEthernetConnected(deviceName string) bool {
content, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/operstate", deviceName)) content, err := os.ReadFile(fmt.Sprintf("/sys/class/net/%s/operstate", deviceName))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@@ -95,7 +95,7 @@ func (p EthernetDisconnect) GetExample() string {
"options": { "options": {
"interfaceName": "eth0", "interfaceName": "eth0",
"waitTillConnected": true "waitTillConnected": true
} },
"actions": [ "actions": [
] ]
} }

View File

@@ -54,7 +54,7 @@ func (p TimeOut) GetExample() string {
"name": "Example Trigger", "name": "Example Trigger",
"options": { "options": {
"duration": 5 "duration": 5
} },
"actions": [ "actions": [
] ]
} }

View File

@@ -96,7 +96,7 @@ func (p UsbDisconnect) GetExample() string {
"options": { "options": {
"deviceId": "ata-Samsung_SSD_860_EVO_1TB_S4AALKWJDI102", "deviceId": "ata-Samsung_SSD_860_EVO_1TB_S4AALKWJDI102",
"waitTillConnected": true "waitTillConnected": true
} },
"actions": [ "actions": [
] ]
} }