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 {
|
type Action interface {
|
||||||
Execute()
|
Execute()
|
||||||
DryExecute()
|
DryExecute()
|
||||||
|
Create(internal.ActionConfig, chan bool) (Action, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DocumentedAction interface {
|
||||||
|
Action
|
||||||
|
internal.Documenter
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stage struct {
|
type Stage struct {
|
||||||
@@ -58,21 +64,15 @@ func (a StagedActions) Execute() {
|
|||||||
a.executeInternal(func(a Action) { a.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) {
|
func NewSingleAction(config internal.ActionConfig, c chan bool) (Action, error) {
|
||||||
if config.Type == "Print" {
|
for _, availableAction := range GetAllActions() {
|
||||||
return NewPrint(config, c)
|
if config.Type == availableAction.GetName() {
|
||||||
}
|
return availableAction.Create(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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("Error parsing config: Action with type %s does not exists", config.Type)
|
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
|
return stagedActions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDocumenters() []internal.Documenter {
|
func GetAllActions() []DocumentedAction {
|
||||||
return []internal.Documenter{
|
return []DocumentedAction{
|
||||||
Printer{},
|
Printer{},
|
||||||
TimeOut{},
|
TimeOut{},
|
||||||
Command{},
|
Command{},
|
||||||
Shutdown{},
|
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() {
|
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
|
p.ActionChan <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Printer) DryExecute() {
|
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
|
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
|
var result Printer
|
||||||
err := json.Unmarshal(config.Options, &result)
|
err := json.Unmarshal(config.Options, &result)
|
||||||
|
|
||||||
|
|||||||
@@ -11,24 +11,24 @@ type Shutdown struct {
|
|||||||
ActionChan chan bool
|
ActionChan chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Shutdown) DryExecute() {
|
func (s Shutdown) DryExecute() {
|
||||||
fmt.Println("Test Shutdown executed...")
|
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 {
|
if err := exec.Command("shutdown", "-h", "now").Run(); err != nil {
|
||||||
fmt.Println("Failed to initiate shutdown:", err)
|
fmt.Println("Failed to initiate shutdown:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Shutdown executed...")
|
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
|
return Shutdown{c}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ func (t TimeOut) DryExecute() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t TimeOut) Execute() {
|
func (t TimeOut) Execute() {
|
||||||
fmt.Printf("Waiting %d seconds\n", t.Duration/time.Second)
|
fmt.Printf("Waiting %d seconds\n", t.Duration)
|
||||||
time.Sleep(t.Duration)
|
time.Sleep(time.Duration(t.Duration) * time.Second)
|
||||||
t.ActionChan <- true
|
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
|
var result TimeOut
|
||||||
err := json.Unmarshal(config.Options, &result)
|
err := json.Unmarshal(config.Options, &result)
|
||||||
|
|
||||||
@@ -45,6 +45,6 @@ func (p TimeOut) GetDescription() string {
|
|||||||
|
|
||||||
func (p TimeOut) GetOptions() []internal.ConfigOption {
|
func (p TimeOut) GetOptions() []internal.ConfigOption {
|
||||||
return []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())
|
result += fmt.Sprintf("\n### %v\nDescription: %v \nValues:\n", act.GetName(), act.GetDescription())
|
||||||
|
|
||||||
for _, opt := range act.GetOptions() {
|
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)
|
opt.Name, opt.Type, opt.Description, opt.Default)
|
||||||
result += "\n\n"
|
result += "\n\n"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,8 +49,7 @@ func (t EthernetDisconnect) Listen() {
|
|||||||
actions.Fire(t.action)
|
actions.Fire(t.action)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func NewTimeOut(d time.Duration, action actions.Action) EthernetDisconnect {
|
func CreateEthernetDisconnect(config internal.KillSwitchConfig) (EthernetDisconnect, error) {
|
||||||
func NewEthernetDisconnect(config internal.KillSwitchConfig) (EthernetDisconnect, error) {
|
|
||||||
result := EthernetDisconnect{
|
result := EthernetDisconnect{
|
||||||
WaitTillConnected: true,
|
WaitTillConnected: true,
|
||||||
}
|
}
|
||||||
@@ -76,6 +75,10 @@ func NewEthernetDisconnect(config internal.KillSwitchConfig) (EthernetDisconnect
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e EthernetDisconnect) Create(config internal.KillSwitchConfig) (Trigger, error) {
|
||||||
|
return CreateEthernetDisconnect(config)
|
||||||
|
}
|
||||||
|
|
||||||
func (p EthernetDisconnect) GetName() string {
|
func (p EthernetDisconnect) GetName() string {
|
||||||
return "EthernetDisconnect"
|
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)
|
actions.Fire(t.action)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTimeOut(config internal.KillSwitchConfig) (TimeOut, error) {
|
func (t TimeOut) Create(config internal.KillSwitchConfig) (Trigger, error) {
|
||||||
var result TimeOut
|
var result TimeOut
|
||||||
err := json.Unmarshal(config.Options, &result)
|
err := json.Unmarshal(config.Options, &result)
|
||||||
|
|
||||||
@@ -50,6 +50,6 @@ func (p TimeOut) GetDescription() string {
|
|||||||
|
|
||||||
func (p TimeOut) GetOptions() []internal.ConfigOption {
|
func (p TimeOut) GetOptions() []internal.ConfigOption {
|
||||||
return []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 {
|
type Trigger interface {
|
||||||
Listen()
|
Listen()
|
||||||
|
Create(internal.KillSwitchConfig) (Trigger, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DocumentedTrigger interface {
|
||||||
|
internal.Documenter
|
||||||
|
Trigger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTrigger(config internal.KillSwitchConfig) (Trigger, error) {
|
func NewTrigger(config internal.KillSwitchConfig) (Trigger, error) {
|
||||||
if config.Type == "Timeout" {
|
for _, availableTrigger := range GetAllTriggers() {
|
||||||
return NewTimeOut(config)
|
if config.Type == availableTrigger.GetName() {
|
||||||
}
|
return availableTrigger.Create(config)
|
||||||
|
}
|
||||||
if config.Type == "EthernetDisconnect" {
|
|
||||||
return NewEthernetDisconnect(config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("Error parsing config: Trigger with type %s does not exists", config.Type)
|
return nil, fmt.Errorf("Error parsing config: Trigger with type %s does not exists", config.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDocumenters() []internal.Documenter {
|
func GetAllTriggers() []DocumentedTrigger {
|
||||||
return []internal.Documenter{
|
return []DocumentedTrigger{
|
||||||
TimeOut{},
|
TimeOut{},
|
||||||
EthernetDisconnect{},
|
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