Skip to content
This repository has been archived by the owner on Sep 5, 2022. It is now read-only.

Commit

Permalink
Add option to skip channel flush
Browse files Browse the repository at this point in the history
Add option to execute system commands async and return success to DCS
immediately
Add option to select InterceptionMode (Pre|Post|Executed)
  • Loading branch information
wilriker committed Oct 19, 2020
1 parent 0bfdeed commit f6f8525
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 38 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Description
This is a small extension to [DuetSoftwareFramework](https://github.com/christhamm/DuetSoftwareFramework)
This is a small extension to [DuetSoftwareFramework](https://github.com/Duet3D/DuetSoftwareFramework)
to execute arbitrary system commands when a user-defined `M-Code` is encountered.

An example usage would be to execute system shutdown on the SBC when a e.g. `M7722` is run.
Expand All @@ -9,15 +9,23 @@ An example usage would be to execute system shutdown on the SBC when a e.g. `M77
$ execonmcode -help
Usage of ./execonmcode:
-command value
Command to execute
Command to execute. This can be specified multiple times.
-debug
Print debug output
-execAsync
Run command to execute async and return success to DCS immediately
-interceptionMode string
Interception mode to use (default "Pre")
-mCode value
Code that will initiate execution of the command
Code that will initiate execution of the command. This can be specified multiple times.
-noFlush
Do not flush the code channel before executing the associated command
-socketPath string
Path to socket (default "/var/run/dsf/dcs.sock")
-trace
Print underlying requests/responses
-version
Show version and exit
```

Starting from version 3 it is possible to provide an arbitrary number of `-mCode` + `-command` tuples. This way a
Expand Down
42 changes: 25 additions & 17 deletions cmd/eom/execonmcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,29 @@ import (
"flag"
"log"
"os"
"strings"

"github.com/Duet3D/DSF-APIs/godsfapi/v3/connection"
"github.com/Duet3D/DSF-APIs/godsfapi/v3/connection/initmessages"
"github.com/wilriker/execonmcode"
)

const (
version = "5.1"
version = "5.2"
)

type settings struct {
socketPath string
mCodes execonmcode.MCodes
commands execonmcode.Commands
debug bool
trace bool
}

func main() {

s := settings{}
s := execonmcode.Settings{}

flag.StringVar(&s.socketPath, "socketPath", connection.FullSocketPath, "Path to socket")
flag.Var(&s.mCodes, "mCode", "Code that will initiate execution of the command")
flag.Var(&s.commands, "command", "Command to execute")
flag.BoolVar(&s.debug, "debug", false, "Print debug output")
flag.BoolVar(&s.trace, "trace", false, "Print underlying requests/responses")
flag.StringVar(&s.SocketPath, "socketPath", connection.FullSocketPath, "Path to socket")
flag.StringVar(&s.InterceptionMode, "interceptionMode", string(initmessages.InterceptionModePre), "Interception mode to use")
flag.Var(&s.MCodes, "mCode", "Code that will initiate execution of the command. This can be specified multiple times.")
flag.Var(&s.Commands, "command", "Command to execute. This can be specified multiple times.")
flag.BoolVar(&s.NoFlush, "noFlush", false, "Do not flush the code channel before executing the associated command")
flag.BoolVar(&s.ExecAsync, "execAsync", false, "Run command to execute async and return success to DCS immediately")
flag.BoolVar(&s.Debug, "debug", false, "Print debug output")
flag.BoolVar(&s.Trace, "trace", false, "Print underlying requests/responses")
version := flag.Bool("version", false, "Show version and exit")
flag.Parse()

Expand All @@ -38,11 +35,22 @@ func main() {
os.Exit(0)
}

if s.mCodes.Len() != s.commands.Len() {
if s.MCodes.Len() != s.Commands.Len() {
log.Fatal("Unequal amount of M-codes and commands given")
}

e := execonmcode.NewExecutor(s.socketPath, s.commands, s.mCodes, s.debug, s.trace)
switch strings.ToLower(s.InterceptionMode) {
case strings.ToLower(string(initmessages.InterceptionModePre)):
s.InterceptionMode = string(initmessages.InterceptionModePre)
case strings.ToLower(string(initmessages.InterceptionModePost)):
s.InterceptionMode = string(initmessages.InterceptionModePost)
case strings.ToLower(string(initmessages.InterceptionModeExecuted)):
s.InterceptionMode = string(initmessages.InterceptionModeExecuted)
default:
log.Fatal("Unsupported InterceptionMode", s.InterceptionMode)
}

e := execonmcode.NewExecutor(s)
err := e.Run()
if err != nil {
log.Fatal(err)
Expand Down
51 changes: 33 additions & 18 deletions executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,44 @@ const (

type Executor struct {
socketPath string
mode initmessages.InterceptionMode
mCodes map[int64]int
commands Commands
execAsync bool
flush bool
debug bool
trace bool
}

func NewExecutor(socketPath string, commands Commands, mCodes MCodes, debug, trace bool) *Executor {
func NewExecutor(s Settings) *Executor {
mc := make(map[int64]int)
for i, m := range mCodes {
for i, m := range s.MCodes {
mc[m] = i
if debug {
cmd, args, err := commands.Get(i)
if s.Debug {
cmd, args, err := s.Commands.Get(i)
if err != nil {
log.Println(m, err)
}
log.Printf("%d: %s %s", m, cmd, strings.Join(args, " "))
}
}
return &Executor{
socketPath: socketPath,
socketPath: s.SocketPath,
mode: initmessages.InterceptionMode(s.InterceptionMode),
mCodes: mc,
commands: commands,
debug: debug,
trace: trace,
commands: s.Commands,
execAsync: s.ExecAsync,
flush: !s.NoFlush,
debug: s.Debug,
trace: s.Trace,
}
}

func (e *Executor) Run() error {

ic := connection.InterceptConnection{}
ic.Debug = e.trace
err := ic.Connect(initmessages.InterceptionModePre, e.socketPath)
err := ic.Connect(e.mode, e.socketPath)
if err != nil {
return err
}
Expand All @@ -77,11 +83,13 @@ func (e *Executor) Run() error {
ic.IgnoreCode()
continue
}
success, err := ic.Flush(c.Channel)
if !success || err != nil {
log.Println("Could not Flush. Cancelling code")
ic.CancelCode()
continue
if e.flush {
success, err := ic.Flush(c.Channel)
if !success || err != nil {
log.Println("Could not Flush. Cancelling code")
ic.CancelCode()
continue
}
}
comd, a, err := e.commands.Get(i)
if err != nil {
Expand All @@ -91,11 +99,18 @@ func (e *Executor) Run() error {
if e.debug {
log.Println("Executing:", cmd)
}
output, err := cmd.CombinedOutput()
if err != nil {
err = ic.ResolveCode(messages.Error, fmt.Sprintf("%s: %s", err.Error(), string(output)))
} else {

// If we should exec async run it as goroutine and return success
if e.execAsync {
go cmd.Run()
err = ic.ResolveCode(messages.Success, "")
} else {
output, err := cmd.CombinedOutput()
if err != nil {
err = ic.ResolveCode(messages.Error, fmt.Sprintf("%s: %s", err.Error(), string(output)))
} else {
err = ic.ResolveCode(messages.Success, "")
}
}
if err != nil {
log.Println("Error executing command:", err)
Expand Down
12 changes: 12 additions & 0 deletions settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package execonmcode

type Settings struct {
SocketPath string
InterceptionMode string
MCodes MCodes
Commands Commands
NoFlush bool
ExecAsync bool
Debug bool
Trace bool
}

0 comments on commit f6f8525

Please sign in to comment.