Compare commits
10 Commits
e8c7df4c18
...
8e8088ab39
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e8088ab39 | |||
| 2b33525ea1 | |||
| d6f09d7c84 | |||
| cec34477c0 | |||
| 15bb4dc862 | |||
| 2df2a00721 | |||
| 0354d86796 | |||
| 64bce5827c | |||
| 74a61c0a51 | |||
| 62aff64f3b |
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
example.json
|
||||
go.sum
|
||||
go.mod
|
||||
gokill
|
||||
output.md
|
||||
thoughts.md
|
||||
@@ -10,6 +10,12 @@ import (
|
||||
type Action interface {
|
||||
Execute()
|
||||
DryExecute()
|
||||
Create(internal.ActionConfig, chan bool) (Action, error)
|
||||
}
|
||||
|
||||
type DocumentedAction interface {
|
||||
Action
|
||||
internal.Documenter
|
||||
}
|
||||
|
||||
type Stage struct {
|
||||
@@ -58,21 +64,15 @@ func (a StagedActions) Execute() {
|
||||
a.executeInternal(func(a Action) { a.Execute() })
|
||||
}
|
||||
|
||||
func (a StagedActions) Create(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||
return StagedActions{}, nil
|
||||
}
|
||||
|
||||
func NewSingleAction(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||
if config.Type == "Print" {
|
||||
return NewPrint(config, c)
|
||||
}
|
||||
|
||||
if config.Type == "Timeout" {
|
||||
return NewTimeOut(config, c)
|
||||
}
|
||||
|
||||
if config.Type == "Command" {
|
||||
return NewCommand(config, c)
|
||||
}
|
||||
|
||||
if config.Type == "Shutdown" {
|
||||
return NewShutdown(config, c)
|
||||
for _, availableAction := range GetAllActions() {
|
||||
if config.Type == availableAction.GetName() {
|
||||
return availableAction.Create(config, c)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Error parsing config: Action with type %s does not exists", config.Type)
|
||||
@@ -111,11 +111,21 @@ func NewAction(config []internal.ActionConfig) (Action, error) {
|
||||
return stagedActions, nil
|
||||
}
|
||||
|
||||
func GetDocumenters() []internal.Documenter {
|
||||
return []internal.Documenter{
|
||||
func GetAllActions() []DocumentedAction {
|
||||
return []DocumentedAction{
|
||||
Printer{},
|
||||
TimeOut{},
|
||||
Command{},
|
||||
Shutdown{},
|
||||
}
|
||||
}
|
||||
|
||||
func GetDocumenters() []internal.Documenter {
|
||||
var result []internal.Documenter
|
||||
|
||||
for _, action := range GetAllActions() {
|
||||
result = append(result, action)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -13,16 +13,16 @@ type Printer struct {
|
||||
}
|
||||
|
||||
func (p Printer) Execute() {
|
||||
fmt.Printf("Print action fires. Message: %s", p.Message)
|
||||
fmt.Printf("Print action fires. Message: %s\n", p.Message)
|
||||
p.ActionChan <- true
|
||||
}
|
||||
|
||||
func (p Printer) DryExecute() {
|
||||
fmt.Printf("Print action fire test. Message: %s", p.Message)
|
||||
fmt.Printf("Print action fire test. Message: %s\n", p.Message)
|
||||
p.ActionChan <- true
|
||||
}
|
||||
|
||||
func NewPrint(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||
func (p Printer) Create(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||
var result Printer
|
||||
err := json.Unmarshal(config.Options, &result)
|
||||
|
||||
|
||||
@@ -11,24 +11,24 @@ type Shutdown struct {
|
||||
ActionChan chan bool
|
||||
}
|
||||
|
||||
func (c Shutdown) DryExecute() {
|
||||
func (s Shutdown) DryExecute() {
|
||||
fmt.Println("Test Shutdown executed...")
|
||||
|
||||
c.ActionChan <- true
|
||||
s.ActionChan <- true
|
||||
|
||||
}
|
||||
|
||||
func (c Shutdown) Execute() {
|
||||
func (s Shutdown) Execute() {
|
||||
if err := exec.Command("shutdown", "-h", "now").Run(); err != nil {
|
||||
fmt.Println("Failed to initiate shutdown:", err)
|
||||
}
|
||||
|
||||
fmt.Println("Shutdown executed...")
|
||||
|
||||
c.ActionChan <- true
|
||||
s.ActionChan <- true
|
||||
}
|
||||
|
||||
func NewShutdown(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||
func (s Shutdown) Create(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||
return Shutdown{c}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ func (t TimeOut) DryExecute() {
|
||||
}
|
||||
|
||||
func (t TimeOut) Execute() {
|
||||
fmt.Printf("Waiting %d seconds\n", t.Duration/time.Second)
|
||||
time.Sleep(t.Duration)
|
||||
fmt.Printf("Waiting %d seconds\n", t.Duration)
|
||||
time.Sleep(time.Duration(t.Duration) * time.Second)
|
||||
t.ActionChan <- true
|
||||
}
|
||||
|
||||
func NewTimeOut(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||
func (t TimeOut) Create(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||
var result TimeOut
|
||||
err := json.Unmarshal(config.Options, &result)
|
||||
|
||||
@@ -45,6 +45,6 @@ func (p TimeOut) GetDescription() string {
|
||||
|
||||
func (p TimeOut) GetOptions() []internal.ConfigOption {
|
||||
return []internal.ConfigOption{
|
||||
{"duration", "string", "duration in seconds", "0"},
|
||||
{"duration", "int", "duration in seconds", "0"},
|
||||
}
|
||||
}
|
||||
|
||||
76
actions/unix_command.go
Normal file
76
actions/unix_command.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"unknown.com/gokill/internal"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
Command string `json:"command"`
|
||||
Args []string `json:"args"`
|
||||
ActionChan chan bool
|
||||
}
|
||||
|
||||
func (c Command) DryExecute() {
|
||||
fmt.Printf("Test Executing Command:\n%s ", c.Command)
|
||||
for _, arg := range c.Args {
|
||||
fmt.Printf("%s ", arg)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
c.ActionChan <- true
|
||||
}
|
||||
|
||||
func (c Command) Execute() {
|
||||
cmd := exec.Command(c.Command, c.Args...)
|
||||
|
||||
stdout, err := cmd.Output()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
fmt.Println(string(stdout[:]))
|
||||
|
||||
c.ActionChan <- true
|
||||
}
|
||||
|
||||
func CreateCommand(config internal.ActionConfig, c chan bool) (Command, error) {
|
||||
result := Command{}
|
||||
|
||||
err := json.Unmarshal(config.Options, &result)
|
||||
|
||||
if err != nil {
|
||||
return Command{}, err
|
||||
}
|
||||
|
||||
if result.Command == "" {
|
||||
return Command{}, internal.OptionMissingError{"command"}
|
||||
}
|
||||
|
||||
result.ActionChan = c
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (cc Command) Create(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||
return CreateCommand(config, c)
|
||||
}
|
||||
|
||||
func (p Command) GetName() string {
|
||||
return "Command"
|
||||
}
|
||||
|
||||
func (p Command) GetDescription() string {
|
||||
return "When triggered executes given command"
|
||||
}
|
||||
|
||||
func (p Command) GetOptions() []internal.ConfigOption {
|
||||
return []internal.ConfigOption{
|
||||
{"command", "string", "command to execute", ""},
|
||||
{"args", "string[]", "args", ""},
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ func GetDocumentation() string {
|
||||
result += fmt.Sprintf("\n### %v\nDescription: %v \nValues:\n", act.GetName(), act.GetDescription())
|
||||
|
||||
for _, opt := range act.GetOptions() {
|
||||
result += fmt.Sprintf("- Name: %v\n\t- Type: %v\n\t- Descr: %v\n\t- Default: %v\n",
|
||||
result += fmt.Sprintf("- Name: **%v**\n\t- Type: %v\n\t- Descr: %v\n\t- Default: %v\n",
|
||||
opt.Name, opt.Type, opt.Description, opt.Default)
|
||||
result += "\n\n"
|
||||
}
|
||||
|
||||
@@ -49,8 +49,7 @@ func (t EthernetDisconnect) Listen() {
|
||||
actions.Fire(t.action)
|
||||
}
|
||||
|
||||
// func NewTimeOut(d time.Duration, action actions.Action) EthernetDisconnect {
|
||||
func NewEthernetDisconnect(config internal.KillSwitchConfig) (EthernetDisconnect, error) {
|
||||
func CreateEthernetDisconnect(config internal.KillSwitchConfig) (EthernetDisconnect, error) {
|
||||
result := EthernetDisconnect{
|
||||
WaitTillConnected: true,
|
||||
}
|
||||
@@ -76,6 +75,10 @@ func NewEthernetDisconnect(config internal.KillSwitchConfig) (EthernetDisconnect
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (e EthernetDisconnect) Create(config internal.KillSwitchConfig) (Trigger, error) {
|
||||
return CreateEthernetDisconnect(config)
|
||||
}
|
||||
|
||||
func (p EthernetDisconnect) GetName() string {
|
||||
return "EthernetDisconnect"
|
||||
}
|
||||
|
||||
82
triggers/ethernet_test.go
Normal file
82
triggers/ethernet_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package triggers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"unknown.com/gokill/internal"
|
||||
)
|
||||
|
||||
func TestEthernetDisconnetConfig(t *testing.T) {
|
||||
type EthernetTest struct {
|
||||
testConfig internal.KillSwitchConfig
|
||||
expectedError error
|
||||
expectedResult EthernetDisconnect
|
||||
}
|
||||
|
||||
testConfigs := []EthernetTest{
|
||||
EthernetTest{
|
||||
testConfig: internal.KillSwitchConfig{
|
||||
Options: []byte("{}"),
|
||||
},
|
||||
|
||||
expectedError: internal.OptionMissingError{"interfaceName"},
|
||||
expectedResult: EthernetDisconnect{},
|
||||
},
|
||||
EthernetTest{
|
||||
testConfig: internal.KillSwitchConfig{
|
||||
Options: []byte(`{
|
||||
"waitTillConnected": false
|
||||
}`),
|
||||
},
|
||||
|
||||
expectedError: internal.OptionMissingError{"interfaceName"},
|
||||
expectedResult: EthernetDisconnect{},
|
||||
},
|
||||
EthernetTest{
|
||||
testConfig: internal.KillSwitchConfig{
|
||||
Options: []byte(`{
|
||||
"interfaceName": "eth0",
|
||||
"waitTillConnected": false
|
||||
}`),
|
||||
},
|
||||
|
||||
expectedError: nil,
|
||||
expectedResult: EthernetDisconnect{WaitTillConnected: false, InterfaceName: "eth0"},
|
||||
},
|
||||
EthernetTest{
|
||||
testConfig: internal.KillSwitchConfig{
|
||||
Options: []byte(`{
|
||||
"interfaceName": "eth0",
|
||||
"waitTillConnected": true
|
||||
}`),
|
||||
},
|
||||
|
||||
expectedError: nil,
|
||||
expectedResult: EthernetDisconnect{WaitTillConnected: true, InterfaceName: "eth0"},
|
||||
},
|
||||
EthernetTest{
|
||||
testConfig: internal.KillSwitchConfig{
|
||||
Options: []byte("{ \"interfaceName\": \"eth0\" }"),
|
||||
},
|
||||
|
||||
expectedError: nil,
|
||||
expectedResult: EthernetDisconnect{WaitTillConnected: true, InterfaceName: "eth0"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testConfig := range testConfigs {
|
||||
result, err := CreateEthernetDisconnect(testConfig.testConfig)
|
||||
|
||||
if err != testConfig.expectedError {
|
||||
t.Errorf("Error was incorrect, got: %s, want: %s.", err, testConfig.expectedError)
|
||||
}
|
||||
|
||||
if result.WaitTillConnected != testConfig.expectedResult.WaitTillConnected {
|
||||
t.Errorf("WaitTillConnected was incorrect, got: %v, want: %v.", result, testConfig.expectedResult)
|
||||
}
|
||||
|
||||
if result.InterfaceName != testConfig.expectedResult.InterfaceName {
|
||||
t.Errorf("InterfaceName was incorrect, got: %v, want: %v.", result, testConfig.expectedResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ func (t TimeOut) Listen() {
|
||||
actions.Fire(t.action)
|
||||
}
|
||||
|
||||
func NewTimeOut(config internal.KillSwitchConfig) (TimeOut, error) {
|
||||
func (t TimeOut) Create(config internal.KillSwitchConfig) (Trigger, error) {
|
||||
var result TimeOut
|
||||
err := json.Unmarshal(config.Options, &result)
|
||||
|
||||
@@ -50,6 +50,6 @@ func (p TimeOut) GetDescription() string {
|
||||
|
||||
func (p TimeOut) GetOptions() []internal.ConfigOption {
|
||||
return []internal.ConfigOption{
|
||||
{"duration", "string", "duration in seconds", "0"},
|
||||
{"duration", "int", "duration in seconds", "0"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,23 +8,38 @@ import (
|
||||
|
||||
type Trigger interface {
|
||||
Listen()
|
||||
Create(internal.KillSwitchConfig) (Trigger, error)
|
||||
}
|
||||
|
||||
type DocumentedTrigger interface {
|
||||
internal.Documenter
|
||||
Trigger
|
||||
}
|
||||
|
||||
func NewTrigger(config internal.KillSwitchConfig) (Trigger, error) {
|
||||
if config.Type == "Timeout" {
|
||||
return NewTimeOut(config)
|
||||
}
|
||||
|
||||
if config.Type == "EthernetDisconnect" {
|
||||
return NewEthernetDisconnect(config)
|
||||
for _, availableTrigger := range GetAllTriggers() {
|
||||
if config.Type == availableTrigger.GetName() {
|
||||
return availableTrigger.Create(config)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Error parsing config: Trigger with type %s does not exists", config.Type)
|
||||
}
|
||||
|
||||
func GetDocumenters() []internal.Documenter {
|
||||
return []internal.Documenter{
|
||||
func GetAllTriggers() []DocumentedTrigger {
|
||||
return []DocumentedTrigger{
|
||||
TimeOut{},
|
||||
EthernetDisconnect{},
|
||||
UsbDisconnect{},
|
||||
}
|
||||
}
|
||||
|
||||
func GetDocumenters() []internal.Documenter {
|
||||
var result []internal.Documenter
|
||||
|
||||
for _, action := range GetAllTriggers() {
|
||||
result = append(result, action)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
96
triggers/usb.go
Normal file
96
triggers/usb.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package triggers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"unknown.com/gokill/actions"
|
||||
"unknown.com/gokill/internal"
|
||||
)
|
||||
|
||||
type UsbDisconnect struct {
|
||||
WaitTillConnected bool `json:"waitTillConnected"`
|
||||
DeviceName string `json:"deviceName"`
|
||||
action actions.Action
|
||||
}
|
||||
|
||||
func isUsbConnected(deviceName string) bool {
|
||||
devicePath := "/dev/disk/by-id/" + deviceName
|
||||
|
||||
_, err := os.Open(devicePath)
|
||||
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (t UsbDisconnect) Listen() {
|
||||
if t.WaitTillConnected {
|
||||
for !isUsbConnected(t.DeviceName) {
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
fmt.Printf("Device %s detected.\n", t.DeviceName)
|
||||
fmt.Println("UsbDisconnect Trigger is Armed")
|
||||
}
|
||||
|
||||
for {
|
||||
if !isUsbConnected(t.DeviceName) {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
actions.Fire(t.action)
|
||||
}
|
||||
|
||||
func CreateUsbDisconnect(config internal.KillSwitchConfig) (UsbDisconnect, error) {
|
||||
result := UsbDisconnect{
|
||||
WaitTillConnected: true,
|
||||
}
|
||||
|
||||
err := json.Unmarshal(config.Options, &result)
|
||||
|
||||
if err != nil {
|
||||
return UsbDisconnect{}, err
|
||||
}
|
||||
|
||||
if result.DeviceName == "" {
|
||||
return UsbDisconnect{}, internal.OptionMissingError{"deviceName"}
|
||||
}
|
||||
|
||||
action, err := actions.NewAction(config.Actions)
|
||||
|
||||
if err != nil {
|
||||
return UsbDisconnect{}, err
|
||||
}
|
||||
|
||||
result.action = action
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (e UsbDisconnect) Create(config internal.KillSwitchConfig) (Trigger, error) {
|
||||
return CreateUsbDisconnect(config)
|
||||
}
|
||||
|
||||
func (p UsbDisconnect) GetName() string {
|
||||
return "UsbDisconnect"
|
||||
}
|
||||
|
||||
func (p UsbDisconnect) GetDescription() string {
|
||||
return "Triggers when given usb drive is disconnected"
|
||||
}
|
||||
|
||||
func (p UsbDisconnect) GetOptions() []internal.ConfigOption {
|
||||
return []internal.ConfigOption{
|
||||
{"waitTillConnected", "bool", "Only trigger when device was connected before", "true"},
|
||||
{"deviceId", "string", "Name of device under /dev/disk/by-id/", "\"\""},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user