1// Copyright 2019 The Pigweed Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may not 4// use this file except in compliance with the License. You may obtain a copy of 5// the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12// License for the specific language governing permissions and limitations under 13// the License. 14 15package pw_target_runner 16 17import ( 18 "fmt" 19 "log" 20 "os" 21 "os/exec" 22 23 pb "pigweed/proto/pw_target_runner/target_runner_pb" 24) 25 26// ExecDeviceRunner is a struct that implements the DeviceRunner interface, 27// running its executables through a command with the path of the executable as 28// an argument. 29type ExecDeviceRunner struct { 30 command []string 31 logger *log.Logger 32} 33 34// NewExecDeviceRunner creates a new ExecDeviceRunner with a custom logger. 35func NewExecDeviceRunner(id int, command []string) *ExecDeviceRunner { 36 logPrefix := fmt.Sprintf("[ExecDeviceRunner %d] ", id) 37 logger := log.New(os.Stdout, logPrefix, log.LstdFlags) 38 return &ExecDeviceRunner{command, logger} 39} 40 41// WorkerStart starts the worker. Part of DeviceRunner interface. 42func (r *ExecDeviceRunner) WorkerStart() error { 43 r.logger.Printf("Starting worker") 44 return nil 45} 46 47// WorkerExit exits the worker. Part of DeviceRunner interface. 48func (r *ExecDeviceRunner) WorkerExit() { 49 r.logger.Printf("Exiting worker") 50} 51 52// HandleRunRequest runs a requested binary by executing the runner's command 53// with the binary path as an argument. The combined stdout and stderr of the 54// command is returned as the run output. 55func (r *ExecDeviceRunner) HandleRunRequest(req *RunRequest) *RunResponse { 56 res := &RunResponse{Status: pb.RunStatus_SUCCESS} 57 58 r.logger.Printf("Running executable %s\n", req.Path) 59 60 // Copy runner command args, appending the binary path to the end. 61 args := append([]string(nil), r.command[1:]...) 62 args = append(args, req.Path) 63 64 cmd := exec.Command(r.command[0], args...) 65 output, err := cmd.CombinedOutput() 66 67 if err != nil { 68 if e, ok := err.(*exec.ExitError); ok { 69 // A nonzero exit status is interpreted as a failure. 70 r.logger.Printf("Command exited with status %d\n", e.ExitCode()) 71 res.Status = pb.RunStatus_FAILURE 72 } else { 73 // Any other error with the command execution is 74 // reported as an internal error to the requester. 75 r.logger.Printf("Command failed: %v\n", err) 76 res.Err = err 77 return res 78 } 79 } 80 81 res.Output = output 82 return res 83} 84