UsbSubSys rework; HIDScript TriggerAction

This commit is contained in:
MaMe82 2018-10-13 15:34:46 +02:00
parent a99836ac08
commit 60717ac478
6 changed files with 185 additions and 89 deletions

Binary file not shown.

View File

@ -1 +0,0 @@
Binary blobs for modified kernel modules

View File

@ -1,8 +1,7 @@
//Endless looping script moving mouse, used to test interrupts and timeouts
while(true) {
moveStepped(200,0);
moveStepped(0,-200);
moveStepped(-200,0);
moveStepped(0,200);
delay(1000);
}
waitLED(ANY);
moveStepped(200,0);
moveStepped(0,-200);
moveStepped(-200,0);
moveStepped(0,200);

View File

@ -32,6 +32,13 @@ const (
cSTORE_PREFIX_TRIGGER_ACTION_SET = "tas_"
)
func NewRpcServerService(root *Service) *server {
return &server{
rootSvc:root,
}
}
type server struct {
rootSvc *Service
@ -116,12 +123,6 @@ func (s *server) DeployTriggerActionSetRemove(ctx context.Context, removeTas *pb
func NewRpcServerService(root *Service) *server {
return &server{
rootSvc:root,
}
}
func (s *server) Start() error {
return nil
}
@ -181,8 +182,6 @@ func (s *server) ListenWiFiStateChanges(ctx context.Context, empty *pb.Empty) (w
}
func (s *server) GetDeployedEthernetInterfaceSettings(ctx context.Context, req *pb.StringMessage) (resp *pb.EthernetInterfaceSettings, err error) {
if mi,err := s.rootSvc.SubSysNetwork.GetManagedInterface(req.Msg); err == nil {
return mi.GetState().CurrentSettings, nil
} else {
@ -283,7 +282,7 @@ func (s *server) FSCreateTempDirOrFile(ctx context.Context, req *pb.TempDirOrFil
}
func (s *server) HIDGetRunningJobState(ctx context.Context, req *pb.HIDScriptJob) (res *pb.HIDRunningJobStateResult, err error) {
targetJob,err := s.rootSvc.SubSysUSB.HidCtl.GetBackgroundJobByID(int(req.Id))
targetJob,err := s.rootSvc.SubSysUSB.HidScriptGetBackgroundJobByID(int(req.Id))
if err != nil { return nil, err }
vmID,_ := targetJob.GetVMId() // ignore error, as VM ID would be -1 in error case
@ -301,11 +300,7 @@ func (s *server) HIDGetRunningJobState(ctx context.Context, req *pb.HIDScriptJob
}
func (s *server) HIDGetRunningScriptJobs(ctx context.Context, rEmpty *pb.Empty) (jobs *pb.HIDScriptJobList, err error) {
if !s.rootSvc.SubSysUSB.Usable { return nil, ErrUsbNotUsable }
if s.rootSvc.SubSysUSB.HidCtl == nil { return nil, rpcErrNoHid}
retJobs,err := s.rootSvc.SubSysUSB.HidCtl.GetAllBackgroundJobs()
retJobs,err := s.rootSvc.SubSysUSB.HidScriptGetAllRunningBackgroundJobs()
if err != nil { return nil, err }
jobs = &pb.HIDScriptJobList{}
for _, aJob := range retJobs {
@ -315,91 +310,75 @@ func (s *server) HIDGetRunningScriptJobs(ctx context.Context, rEmpty *pb.Empty)
}
func (s *server) HIDCancelAllScriptJobs(ctx context.Context, rEmpty *pb.Empty) (empty *pb.Empty, err error) {
empty = &pb.Empty{}
if s.rootSvc.SubSysUSB.HidCtl == nil { return empty, rpcErrNoHid}
// Try to find script
s.rootSvc.SubSysUSB.HidCtl.CancelAllBackgroundJobs()
err = s.rootSvc.SubSysUSB.HidScriptCancelAllRunningBackgroundJobs()
return
}
func (s *server) HIDCancelScriptJob(ctx context.Context, sJob *pb.HIDScriptJob) (empty *pb.Empty, err error) {
empty = &pb.Empty{}
if s.rootSvc.SubSysUSB.HidCtl == nil { return empty, rpcErrNoHid}
// Try to find script
job,err := s.rootSvc.SubSysUSB.HidCtl.GetBackgroundJobByID(int(sJob.Id))
if err != nil { return empty, err }
job,err := s.rootSvc.SubSysUSB.HidScriptGetBackgroundJobByID(int(sJob.Id))
if err != nil { return nil, err }
job.Cancel()
return
}
func (s *server) HIDRunScript(ctx context.Context, scriptReq *pb.HIDScriptRequest) (scriptRes *pb.HIDScriptResult, err error) {
if s.rootSvc.SubSysUSB.HidCtl == nil { return nil, rpcErrNoHid}
err = s.rootSvc.SubSysUSB.HidScriptUsable()
if err != nil { return }
if scriptFile, err := ioutil.ReadFile(scriptReq.ScriptPath); err != nil {
scriptFile, err := ioutil.ReadFile(scriptReq.ScriptPath)
if err != nil {
return nil, errors.New(fmt.Sprintf("Couldn't load HIDScript '%s': %v\n", scriptReq.ScriptPath, err))
} else {
//jobCtx := context.Background()
jobCtx := ctx //we want to interrupt the script if the gRPC client cancels
// ToDo: we don't retrieve the cancelFunc which should be called to free resources. Solution: use withCancel context and call cancel by go routine on timeout
if scriptReq.TimeoutSeconds > 0 { jobCtx,_ = context.WithTimeout(jobCtx, time.Second * time.Duration(scriptReq.TimeoutSeconds))}
}
// ToDo: we don't retrieve the cancelFunc which should be called to free resources. Solution: use withCancel context and call cancel by go routine on timeout
if scriptReq.TimeoutSeconds > 0 { ctx,_ = context.WithTimeout(ctx, time.Second * time.Duration(scriptReq.TimeoutSeconds))}
scriptVal,err := s.rootSvc.SubSysUSB.HidCtl.RunScript(jobCtx, string(scriptFile))
if err != nil { return nil,err }
val,_ := scriptVal.Export() //Convert to Go representation, error is always nil
jsonVal,err := json.Marshal(val)
if err != nil {
return nil, errors.New(fmt.Sprintf("Script seems to have succeeded but result couldn't be converted to JSON: %v\n", err))
}
val,err := s.rootSvc.SubSysUSB.HidScriptRun(ctx, string(scriptFile))
if err != nil { return nil,err }
if jsonVal,err := json.Marshal(val); err == nil {
scriptRes = &pb.HIDScriptResult{
IsFinished: true,
Job: &pb.HIDScriptJob{Id:0},
ResultJson: string(jsonVal),
}
return scriptRes,nil
} else {
return nil, errors.New(fmt.Sprintf("Script seems to have succeeded but result couldn't be converted to JSON: %v\n", err))
}
}
func (s *server) HIDRunScriptJob(ctx context.Context, scriptReq *pb.HIDScriptRequest) (rJob *pb.HIDScriptJob, err error) {
if s.rootSvc.SubSysUSB.HidCtl == nil { return nil, rpcErrNoHid}
err = s.rootSvc.SubSysUSB.HidScriptUsable()
if err != nil { return }
if scriptFile, err := ioutil.ReadFile(scriptReq.ScriptPath); err != nil {
scriptFile, err := ioutil.ReadFile(scriptReq.ScriptPath)
if err != nil {
return nil, errors.New(fmt.Sprintf("Couldn't load HIDScript '%s': %v\n", scriptReq.ScriptPath, err))
} else {
//Note: Don't use the gRPC context, it would cancel after this call and thus interrupt the job immediately
jobCtx := context.Background()
// ToDo: we don't retrieve the cancelFunc which should be called to free resources. Solution: use withCancel context and call cancel by go routine on timeout
if scriptReq.TimeoutSeconds > 0 { jobCtx,_ = context.WithTimeout(jobCtx, time.Second * time.Duration(scriptReq.TimeoutSeconds))}
job,err := s.rootSvc.SubSysUSB.HidCtl.StartScriptAsBackgroundJob(jobCtx, string(scriptFile))
if err != nil { return nil,err }
rJob = &pb.HIDScriptJob{
Id: uint32(job.Id),
}
return rJob,nil
}
return
//Note: Don't use the gRPC context, it would cancel after this call and thus interrupt the job immediately
jobCtx := context.Background()
// ToDo: we don't retrieve the cancelFunc which should be called to free resources. Solution: use withCancel context and call cancel by go routine on timeout
if scriptReq.TimeoutSeconds > 0 { jobCtx,_ = context.WithTimeout(jobCtx, time.Second * time.Duration(scriptReq.TimeoutSeconds))}
job,err := s.rootSvc.SubSysUSB.HidScriptStartBackground(jobCtx, string(scriptFile))
if err != nil { return nil,err }
rJob = &pb.HIDScriptJob{
Id: uint32(job.Id),
}
return rJob,nil
}
func (s *server) HIDGetScriptJobResult(ctx context.Context, sJob *pb.HIDScriptJob) (scriptRes *pb.HIDScriptResult, err error) {
if s.rootSvc.SubSysUSB.HidCtl == nil { return nil, rpcErrNoHid}
// Try to find script
job,err := s.rootSvc.SubSysUSB.HidCtl.GetBackgroundJobByID(int(sJob.Id))
if err != nil { return scriptRes, err }
job,err := s.rootSvc.SubSysUSB.HidScriptGetBackgroundJobByID(int(sJob.Id))
if err != nil { return nil, err }
//ToDo: check impact/behavior, because ctx is provided by gRPC server
scriptVal,err := s.rootSvc.SubSysUSB.HidCtl.WaitBackgroundJobResult(ctx, job)
val,err := s.rootSvc.SubSysUSB.HidScriptWaitBackgroundJobResult(ctx, job)
if err != nil { return nil,err }
val,_ := scriptVal.Export() //Convert to Go representation, error is always nil
jsonVal,err := json.Marshal(val)
if err != nil {
return nil, errors.New(fmt.Sprintf("Script seems to have succeeded but result couldn't be converted to JSON: %v\n", err))
@ -410,7 +389,6 @@ func (s *server) HIDGetScriptJobResult(ctx context.Context, sJob *pb.HIDScriptJo
ResultJson: string(jsonVal),
}
return scriptRes,nil
return
}

View File

@ -3,12 +3,14 @@
package service
import (
"context"
"errors"
"fmt"
"github.com/mame82/P4wnP1_go/common"
"github.com/mame82/P4wnP1_go/common_web"
pb "github.com/mame82/P4wnP1_go/proto"
"github.com/mame82/P4wnP1_go/service/util"
"io/ioutil"
"sync"
)
@ -352,7 +354,65 @@ func (tam *TriggerActionManager) executeActionStartHidScript(evt *pb.Event, ta *
fmt.Printf("Trigger '%s' fired -> executing action '%s' ('%s')\n", triggerName, actionName, action.ScriptName)
// ToDo: Implement
scriptPath := PATH_HID_SCRIPTS + "/" + action.ScriptName
preScript := fmt.Sprintf("TRIGGER='%s';\n", triggerName)
switch tt {
case triggerTypeGpioIn:
gpioPin := ta.Trigger.(*pb.TriggerAction_GpioIn).GpioIn.GpioNum
gpioPinName := pb.GPIONum_name[int32(gpioPin)]
preScript += fmt.Sprintf("GPIO_PIN=%s;\n", gpioPinName)
case triggerTypeGroupReceiveSequence:
groupName := ta.Trigger.(*pb.TriggerAction_GroupReceiveSequence).GroupReceiveSequence.GroupName
values := ta.Trigger.(*pb.TriggerAction_GroupReceiveSequence).GroupReceiveSequence.Values
// create bash array of values
jsArray := "["
for idx,v := range values {
if idx >= len(values) - 1 {
jsArray += fmt.Sprintf("%d", v)
} else {
jsArray += fmt.Sprintf("%d, ", v)
}
}
jsArray += "]"
preScript += fmt.Sprintf("GROUP='%s';\n", groupName)
preScript += fmt.Sprintf("var VALUES=%s;\n", jsArray)
case triggerTypeGroupReceive:
groupName := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.GroupName
value := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.Value
preScript += fmt.Sprintf("GROUP='%s';\n", groupName)
preScript += fmt.Sprintf("VALUE=%d;\n", value)
case triggerTypeDhcpLeaseGranted:
iface := evt.Values[1].GetTstring()
mac := evt.Values[2].GetTstring()
ip := evt.Values[3].GetTstring()
preScript += fmt.Sprintf("DHCP_LEASE_IFACE=%s;\n", iface)
preScript += fmt.Sprintf("DHCP_LEASE_MAC=%s;\n", mac)
preScript += fmt.Sprintf("DHCP_LEASE_IP=%s;\n", ip)
case triggerTypeSshLogin:
loginUser := evt.Values[1].GetTstring()
preScript += fmt.Sprintf("SSH_LOGIN_USER=%s;\n", loginUser)
}
err := tam.rootSvc.SubSysUSB.HidScriptUsable()
if err != nil { return }
scriptFile, err := ioutil.ReadFile(scriptPath)
if err != nil {
fmt.Printf("Couldn't load HIDScript '%s': %v\n", scriptPath, err)
return
}
newScriptFile := preScript + string(scriptFile)
_,err = tam.rootSvc.SubSysUSB.HidScriptStartBackground(context.Background(), newScriptFile)
if err != nil {
fmt.Printf("Couldn't start HIDScript as background job'%s': %v\n", action.ScriptName, err)
return
}
return
}
func (tam *TriggerActionManager) executeActionDeploySettingsTemplate(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType, action *pb.ActionDeploySettingsTemplate) {
@ -395,7 +455,7 @@ func (tam *TriggerActionManager) executeActionBashScript(evt *pb.Event, ta *pb.T
value := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.Value
env = append(env,
fmt.Sprintf("GROUP='%s'", groupName),
fmt.Sprintf("VALUE=%s", value),
fmt.Sprintf("VALUE=%d", value),
)
case triggerTypeDhcpLeaseGranted:
iface := evt.Values[1].GetTstring()

View File

@ -8,13 +8,13 @@ import (
"os/exec"
"strings"
pb "github.com/mame82/P4wnP1_go/proto"
"time"
"context"
"fmt"
"github.com/mame82/P4wnP1_go/hid"
pb "github.com/mame82/P4wnP1_go/proto"
"net"
"regexp"
"github.com/mame82/P4wnP1_go/hid"
"context"
"time"
)
const (
@ -76,6 +76,7 @@ const (
var (
ErrUsbNotUsable = errors.New("USB subsystem not available")
ErrHidNotUsable = errors.New("HIDScript not available (mouse and keyboard disabled)")
rp_usbHidDevName = regexp.MustCompile("(?m)DEVNAME=(.*)\n")
)
@ -91,7 +92,7 @@ type UsbGadgetManager struct {
State *UsbManagerState
// ToDo: variable, indicating if HIDScript is usable
HidCtl *hid.HIDController // Points to an HID controller instance only if keyboard and/or mouse are enabled, nil otherwise
hidCtl *hid.HIDController // Points to an HID controller instance only if keyboard and/or mouse are enabled, nil otherwise
}
func (gm *UsbGadgetManager) HandleEvent(event hid.Event) {
@ -99,6 +100,65 @@ func (gm *UsbGadgetManager) HandleEvent(event hid.Event) {
gm.RootSvc.SubSysEvent.Emit(ConstructEventHID(event))
}
func (gm *UsbGadgetManager) HidScriptUsable() error {
if gm.hidCtl == nil {
return ErrHidNotUsable
}
return nil
}
func (gm *UsbGadgetManager) HidScriptRun(ctx context.Context, scriptContent string) (result interface{}, err error) {
err = gm.HidScriptUsable()
if err != nil { return }
scriptVal,err := gm.hidCtl.RunScript(ctx, scriptContent)
if err != nil { return nil, err}
return scriptVal.Export()
}
func (gm *UsbGadgetManager) HidScriptStartBackground(ctx context.Context, scriptContent string) (job *hid.AsyncOttoJob, err error) {
err = gm.HidScriptUsable()
if err != nil { return }
return gm.hidCtl.StartScriptAsBackgroundJob(ctx, scriptContent)
}
//WaitBackgroundJobResult(ctx context.Context, job *AsyncOttoJob) (val otto.Value, err error) {
func (gm *UsbGadgetManager) HidScriptWaitBackgroundJobResult(ctx context.Context, job *hid.AsyncOttoJob) (result interface{}, err error) {
err = gm.HidScriptUsable()
if err != nil { return }
scriptVal,err := gm.hidCtl.WaitBackgroundJobResult(ctx, job)
if err != nil { return nil, err}
return scriptVal.Export()
}
func (gm *UsbGadgetManager) HidScriptGetBackgroundJobByID(id int) (job *hid.AsyncOttoJob, err error) {
err = gm.HidScriptUsable()
if err != nil { return }
return gm.hidCtl.GetBackgroundJobByID(id)
}
func (gm *UsbGadgetManager) HidScriptGetAllRunningBackgroundJobs() (jobs []*hid.AsyncOttoJob, err error) {
err = gm.HidScriptUsable()
if err != nil { return }
return gm.hidCtl.GetAllBackgroundJobs()
}
func (gm *UsbGadgetManager) HidScriptCancelAllRunningBackgroundJobs() (err error) {
err = gm.HidScriptUsable()
if err != nil { return }
gm.hidCtl.CancelAllBackgroundJobs()
return
}
func NewUSBGadgetManager(rooSvc *Service) (newUGM *UsbGadgetManager, err error) {
newUGM = &UsbGadgetManager{
RootSvc: rooSvc,
@ -674,16 +734,16 @@ func (gm *UsbGadgetManager) DeployGadgetSettings(settings *pb.GadgetSettings) er
devPathMouse := gm.State.DevicePath[USB_FUNCTION_HID_MOUSE_name]
var errH error
gm.HidCtl, errH = hid.NewHIDController(context.Background(), devPathKeyboard, PATH_KEYBOARD_LANGUAGE_MAPS, devPathMouse)
gm.HidCtl.SetEventHandler(gm)
gm.hidCtl, errH = hid.NewHIDController(context.Background(), devPathKeyboard, PATH_KEYBOARD_LANGUAGE_MAPS, devPathMouse)
gm.hidCtl.SetEventHandler(gm)
if errH != nil {
log.Printf("ERROR: Couldn't bring up an instance of HIDController for keyboard: '%s', mouse: '%s' and mapping path '%s'\nReason: %v\n", devPathKeyboard, devPathMouse, PATH_KEYBOARD_LANGUAGE_MAPS, errH)
} else {
log.Printf("HIDController for keyboard: '%s', mouse: '%s' and mapping path '%s' initialized\n", devPathKeyboard, devPathMouse, PATH_KEYBOARD_LANGUAGE_MAPS)
}
} else {
if gm.HidCtl != nil { gm.HidCtl.Abort() }
gm.HidCtl = nil
if gm.hidCtl != nil { gm.hidCtl.Abort() }
gm.hidCtl = nil
log.Printf("HIDController for keyboard / mouse disabled\n")
}
}
@ -765,8 +825,8 @@ func (gm *UsbGadgetManager) DestroyAllGadgets() error {
}
}
if gm.HidCtl != nil { gm.HidCtl.Abort() }
gm.HidCtl = nil
if gm.hidCtl != nil { gm.hidCtl.Abort() }
gm.hidCtl = nil
log.Printf("HIDController for keyboard / mouse disabled\n")
return nil