@@ -11,12 +11,13 @@ import (
1111 "math/rand/v2"
1212 "net"
1313 "os"
14- "os/exec"
1514 "os/user"
1615 "path/filepath"
1716 "runtime"
1817 "strings"
1918
19+ "github.com/arduino/go-paths-helper"
20+
2021 "github.com/bcmi-labs/orchestrator/pkg/board/remote"
2122)
2223
@@ -45,7 +46,11 @@ func FromHost(host string, adbPath string) (*ADBConnection, error) {
4546 if adbPath == "" {
4647 adbPath = FindAdbPath ()
4748 }
48- if err := exec .Command (adbPath , "connect" , host ).Run (); err != nil {
49+ cmd , err := paths .NewProcess (nil , adbPath , "connect" , host )
50+ if err != nil {
51+ return nil , err
52+ }
53+ if err := cmd .Run (); err != nil {
4954 return nil , fmt .Errorf ("failed to connect to ADB host %s: %w" , host , err )
5055 }
5156 return FromSerial (host , adbPath )
@@ -59,7 +64,11 @@ func (a *ADBConnection) Forward(ctx context.Context, remotePort int) (int, error
5964
6065 local := fmt .Sprintf ("tcp:%d" , hostAvailablePort )
6166 remote := fmt .Sprintf ("tcp:%d" , remotePort )
62- if err := exec .CommandContext (ctx , a .adbPath , "-s" , a .host , "forward" , local , remote ).Run (); err != nil { // nolint:gosec
67+ cmd , err := paths .NewProcess (nil , a .adbPath , "-s" , a .host , "forward" , local , remote )
68+ if err != nil {
69+ return hostAvailablePort , err
70+ }
71+ if err := cmd .RunWithinContext (ctx ); err != nil {
6372 return hostAvailablePort , fmt .Errorf (
6473 "failed to forward ADB port %s to %s: %w" ,
6574 local ,
@@ -71,15 +80,22 @@ func (a *ADBConnection) Forward(ctx context.Context, remotePort int) (int, error
7180}
7281
7382func (a * ADBConnection ) ForwardKillAll (ctx context.Context ) error {
74- if err := exec .CommandContext (ctx , a .adbPath , "-s" , a .host , "killforward-all" ).Run (); err != nil { // nolint:gosec
83+ cmd , err := paths .NewProcess (nil , a .adbPath , "-s" , a .host , "killforward-all" )
84+ if err != nil {
85+ return err
86+ }
87+ if err := cmd .RunWithinContext (ctx ); err != nil {
7588 return fmt .Errorf ("failed to kill all ADB forwarded ports: %w" , err )
7689 }
7790 return nil
7891}
7992
8093func (a * ADBConnection ) List (path string ) ([]remote.FileInfo , error ) {
81- cmd := exec .Command (a .adbPath , "-s" , a .host , "shell" , "ls" , "-la" , path ) // nolint:gosec
82- cmd .Stderr = os .Stdout
94+ cmd , err := paths .NewProcess (nil , a .adbPath , "-s" , a .host , "shell" , "ls" , "-la" , path )
95+ if err != nil {
96+ return nil , err
97+ }
98+ cmd .RedirectStderrTo (os .Stdout )
8399 output , err := cmd .StdoutPipe ()
84100 if err != nil {
85101 return nil , err
@@ -123,7 +139,10 @@ func (a *ADBConnection) List(path string) ([]remote.FileInfo, error) {
123139}
124140
125141func (a * ADBConnection ) Stats (path string ) (remote.FileInfo , error ) {
126- cmd := exec .Command (a .adbPath , "-s" , a .host , "shell" , "file" , path ) // nolint:gosec
142+ cmd , err := paths .NewProcess (nil , a .adbPath , "-s" , a .host , "shell" , "file" , path )
143+ if err != nil {
144+ return remote.FileInfo {}, err
145+ }
127146 output , err := cmd .StdoutPipe ()
128147 if err != nil {
129148 return remote.FileInfo {}, err
@@ -167,28 +186,34 @@ func (a *ADBConnection) WriteFile(r io.Reader, path string) error {
167186}
168187
169188func (a * ADBConnection ) MkDirAll (path string ) error {
170- cmd := exec .Command (a .adbPath , "-s" , a .host , "shell" , "install" , "-o" , username , "-g" , username , "-m" , "755" , "-d" , path ) // nolint:gosec
171- out , err := cmd .CombinedOutput ()
189+ cmd , err := paths .NewProcess (nil , a .adbPath , "-s" , a .host , "shell" , "install" , "-o" , username , "-g" , username , "-m" , "755" , "-d" , path )
190+ if err != nil {
191+ return err
192+ }
193+ stdout , err := cmd .RunAndCaptureCombinedOutput (context .Background ())
172194 if err != nil {
173- return fmt .Errorf ("failed to create directory %q: %w: %s" , path , err , out )
195+ return fmt .Errorf ("failed to create directory %q: %w: %s" , path , err , string ( stdout ) )
174196 }
175197 return nil
176198}
177199
178200func (a * ADBConnection ) Remove (path string ) error {
179- cmd := exec .Command (a .adbPath , "-s" , a .host , "shell" , "rm" , "-r" , path ) // nolint:gosec
180- out , err := cmd .CombinedOutput ()
201+ cmd , err := paths .NewProcess (nil , a .adbPath , "-s" , a .host , "shell" , "rm" , "-r" , path ) // nolint:gosec
202+ if err != nil {
203+ return err
204+ }
205+ stdout , err := cmd .RunAndCaptureCombinedOutput (context .Background ())
181206 if err != nil {
182- return fmt .Errorf ("failed to remove path %q: %w: %s" , path , err , out )
207+ return fmt .Errorf ("failed to remove path %q: %w: %s" , path , err , string ( stdout ) )
183208 }
184209 return nil
185210}
186211
187212type ADBCommand struct {
188- cmd * exec. Cmd
213+ cmd * paths. Process
189214}
190215
191- func (a * ADBConnection ) GetCmd (ctx context. Context , cmd string , args ... string ) remote.Cmder {
216+ func (a * ADBConnection ) GetCmd (cmd string , args ... string ) remote.Cmder {
192217 for i , arg := range args {
193218 if strings .Contains (arg , " " ) {
194219 args [i ] = fmt .Sprintf ("%q" , arg )
@@ -197,42 +222,48 @@ func (a *ADBConnection) GetCmd(ctx context.Context, cmd string, args ...string)
197222
198223 // TODO: fix command injection vulnerability
199224 var cmds []string
200- cmds = append (cmds , "-s" , a .host , "shell" , cmd )
201- cmds = append (cmds , args ... )
202-
203- cmdd := exec .CommandContext (ctx , a .adbPath , cmds ... ) // nolint:gosec
204- return & ADBCommand {
205- cmd : cmdd ,
225+ cmds = append (cmds , a .adbPath , "-s" , a .host , "shell" , cmd )
226+ if len (args ) > 0 {
227+ cmds = append (cmds , args ... )
206228 }
229+
230+ command , _ := paths .NewProcess (nil , cmds ... )
231+ return & ADBCommand {cmd : command }
207232}
208233
209- func (a * ADBCommand ) Run () error {
210- return a .cmd .Run ( )
234+ func (a * ADBCommand ) Run (ctx context. Context ) error {
235+ return a .cmd .RunWithinContext ( ctx )
211236}
212237
213- func (a * ADBCommand ) Output () ([]byte , error ) {
214- return a .cmd .CombinedOutput ( )
238+ func (a * ADBCommand ) Output (ctx context. Context ) ([]byte , error ) {
239+ return a .cmd .RunAndCaptureCombinedOutput ( ctx )
215240}
216241
217- func (a * ADBCommand ) Interactive () (io.WriteCloser , io.Reader , remote.Closer , error ) {
218- a .cmd .Stderr = a .cmd .Stdout // Redirect stderr to stdout
242+ func (a * ADBCommand ) Interactive () (io.WriteCloser , io.Reader , io.Reader , remote.Closer , error ) {
219243 stdin , err := a .cmd .StdinPipe ()
220244 if err != nil {
221- return nil , nil , nil , fmt .Errorf ("failed to get stdin pipe: %w" , err )
245+ return nil , nil , nil , nil , fmt .Errorf ("failed to get stdin pipe: %w" , err )
222246 }
223247 stdout , err := a .cmd .StdoutPipe ()
224248 if err != nil {
225- return nil , nil , nil , fmt .Errorf ("failed to get stdout pipe: %w" , err )
249+ return nil , nil , nil , nil , fmt .Errorf ("failed to get stdout pipe: %w" , err )
250+ }
251+ stderr , err := a .cmd .StderrPipe ()
252+ if err != nil {
253+ return nil , nil , nil , nil , fmt .Errorf ("failed to get stderr pipe: %w" , err )
226254 }
227255
228256 if err := a .cmd .Start (); err != nil {
229- return nil , nil , nil , fmt .Errorf ("failed to start command: %w" , err )
257+ return nil , nil , nil , nil , fmt .Errorf ("failed to start command: %w" , err )
230258 }
231259
232- return stdin , stdout , func () error {
260+ return stdin , stdout , stderr , func () error {
233261 if err := stdout .Close (); err != nil {
234262 return fmt .Errorf ("failed to close stdout pipe: %w" , err )
235263 }
264+ if err := stderr .Close (); err != nil {
265+ return fmt .Errorf ("failed to close stderr pipe: %w" , err )
266+ }
236267 if err := a .cmd .Wait (); err != nil {
237268 return fmt .Errorf ("command failed: %w" , err )
238269 }
0 commit comments