@@ -60,12 +60,24 @@ type Target interface {
60
60
GetUserPath () (path string , err error )
61
61
62
62
// RunCommand runs the specified command on the target.
63
+ // Arguments:
64
+ // - cmd: the command to run
65
+ // - timeout: the maximum time allowed for the command to run (zero means no timeout)
66
+ // - reuseSSHConnection: whether to reuse the SSH connection for the command (only relevant for RemoteTarget)
63
67
// It returns the standard output, standard error, exit code, and any error that occurred.
64
- RunCommand (cmd * exec.Cmd , timeout int ) (stdout string , stderr string , exitCode int , err error )
68
+ RunCommand (cmd * exec.Cmd , timeout int , reuseSSHConnection bool ) (stdout string , stderr string , exitCode int , err error )
65
69
66
70
// RunCommandAsync runs the specified command on the target in an asynchronous manner.
71
+ // Arguments:
72
+ // - cmd: the command to run
73
+ // - timeout: the maximum time allowed for the command to run (zero means no timeout)
74
+ // - reuseSSHConnection: whether to reuse the SSH connection for the command (only relevant for RemoteTarget)
75
+ // - stdoutChannel: a channel to send the standard output of the command
76
+ // - stderrChannel: a channel to send the standard error of the command
77
+ // - exitcodeChannel: a channel to send the exit code of the command
78
+ // - cmdChannel: a channel to send the command that was run
67
79
// It returns any error that occurred.
68
- RunCommandAsync (cmd * exec.Cmd , stdoutChannel chan string , stderrChannel chan string , exitcodeChannel chan int , timeout int , cmdChannel chan * exec.Cmd ) error
80
+ RunCommandAsync (cmd * exec.Cmd , timeout int , reuseSSHConnection bool , stdoutChannel chan string , stderrChannel chan string , exitcodeChannel chan int , cmdChannel chan * exec.Cmd ) error
69
81
70
82
// PushFile transfers a file from the local system to the target.
71
83
// It returns any error that occurred.
@@ -171,16 +183,16 @@ func (t *RemoteTarget) SetSshPass(sshPass string) {
171
183
172
184
// RunCommand executes the given command with a timeout and returns the standard output,
173
185
// standard error, exit code, and any error that occurred.
174
- func (t * LocalTarget ) RunCommand (cmd * exec.Cmd , timeout int ) (stdout string , stderr string , exitCode int , err error ) {
186
+ func (t * LocalTarget ) RunCommand (cmd * exec.Cmd , timeout int , argNotUsed bool ) (stdout string , stderr string , exitCode int , err error ) {
175
187
input := ""
176
188
if t .sudo != "" && len (cmd .Args ) > 2 && cmd .Args [0 ] == "sudo" && strings .HasPrefix (cmd .Args [1 ], "-" ) && strings .Contains (cmd .Args [1 ], "S" ) { // 'sudo -S' gets password from stdin
177
189
input = t .sudo + "\n "
178
190
}
179
191
return runLocalCommandWithInputWithTimeout (cmd , input , timeout )
180
192
}
181
193
182
- func (t * RemoteTarget ) RunCommand (cmd * exec.Cmd , timeout int ) (stdout string , stderr string , exitCode int , err error ) {
183
- localCommand := t .prepareLocalCommand (cmd , false )
194
+ func (t * RemoteTarget ) RunCommand (cmd * exec.Cmd , timeout int , reuseSSHConnection bool ) (stdout string , stderr string , exitCode int , err error ) {
195
+ localCommand := t .prepareLocalCommand (cmd , reuseSSHConnection )
184
196
return runLocalCommandWithInputWithTimeout (localCommand , "" , timeout )
185
197
}
186
198
@@ -190,15 +202,15 @@ func (t *RemoteTarget) RunCommand(cmd *exec.Cmd, timeout int) (stdout string, st
190
202
// and the exit code is sent to the exitcodeChannel.
191
203
// The timeout parameter specifies the maximum time allowed for the command to run.
192
204
// Returns an error if there was a problem running the command.
193
- func (t * LocalTarget ) RunCommandAsync (cmd * exec.Cmd , stdoutChannel chan string , stderrChannel chan string , exitcodeChannel chan int , timeout int , cmdChannel chan * exec.Cmd ) (err error ) {
205
+ func (t * LocalTarget ) RunCommandAsync (cmd * exec.Cmd , timeout int , argNotUsed bool , stdoutChannel chan string , stderrChannel chan string , exitcodeChannel chan int , cmdChannel chan * exec.Cmd ) (err error ) {
194
206
localCommand := cmd
195
207
cmdChannel <- localCommand
196
208
err = runLocalCommandWithInputWithTimeoutAsync (localCommand , stdoutChannel , stderrChannel , exitcodeChannel , "" , timeout )
197
209
return
198
210
}
199
211
200
- func (t * RemoteTarget ) RunCommandAsync (cmd * exec.Cmd , stdoutChannel chan string , stderrChannel chan string , exitcodeChannel chan int , timeout int , cmdChannel chan * exec.Cmd ) (err error ) {
201
- localCommand := t .prepareLocalCommand (cmd , true )
212
+ func (t * RemoteTarget ) RunCommandAsync (cmd * exec.Cmd , timeout int , reuseSSHConnection bool , stdoutChannel chan string , stderrChannel chan string , exitcodeChannel chan int , cmdChannel chan * exec.Cmd ) (err error ) {
213
+ localCommand := t .prepareLocalCommand (cmd , reuseSSHConnection )
202
214
cmdChannel <- localCommand
203
215
err = runLocalCommandWithInputWithTimeoutAsync (localCommand , stdoutChannel , stderrChannel , exitcodeChannel , "" , timeout )
204
216
return
@@ -266,7 +278,7 @@ func (t *RemoteTarget) CreateTempDirectory(rootDir string) (tempDir string, err
266
278
root = fmt .Sprintf ("--tmpdir=%s" , rootDir )
267
279
}
268
280
cmd := exec .Command ("mktemp" , "-d" , "-t" , root , "perfspect.tmp.XXXXXXXXXX" , "|" , "xargs" , "realpath" )
269
- tempDir , _ , _ , err = t .RunCommand (cmd , 0 )
281
+ tempDir , _ , _ , err = t .RunCommand (cmd , 0 , true )
270
282
tempDir = strings .TrimSpace (tempDir )
271
283
t .tempDir = tempDir
272
284
return
@@ -331,7 +343,7 @@ func (t *LocalTarget) CreateDirectory(baseDir string, targetDir string) (dir str
331
343
func (t * RemoteTarget ) CreateDirectory (baseDir string , targetDir string ) (dir string , err error ) {
332
344
dir = filepath .Join (baseDir , targetDir )
333
345
cmd := exec .Command ("mkdir" , dir )
334
- _ , _ , _ , err = t .RunCommand (cmd , 0 )
346
+ _ , _ , _ , err = t .RunCommand (cmd , 0 , true )
335
347
return
336
348
}
337
349
@@ -348,7 +360,7 @@ func (t *LocalTarget) RemoveDirectory(targetDir string) (err error) {
348
360
func (t * RemoteTarget ) RemoveDirectory (targetDir string ) (err error ) {
349
361
if targetDir != "" {
350
362
cmd := exec .Command ("rm" , "-rf" , targetDir )
351
- _ , _ , _ , err = t .RunCommand (cmd , 0 )
363
+ _ , _ , _ , err = t .RunCommand (cmd , 0 , true )
352
364
}
353
365
return
354
366
}
@@ -360,7 +372,7 @@ func (t *LocalTarget) CanConnect() bool {
360
372
361
373
func (t * RemoteTarget ) CanConnect () bool {
362
374
cmd := exec .Command ("exit" , "0" )
363
- _ , _ , _ , err := t .RunCommand (cmd , 5 )
375
+ _ , _ , _ , err := t .RunCommand (cmd , 5 , true )
364
376
return err == nil
365
377
}
366
378
@@ -388,14 +400,14 @@ func (t *LocalTarget) CanElevatePrivileges() bool {
388
400
slog .Error ("error writing sudo password" , slog .String ("error" , err .Error ()))
389
401
}
390
402
}()
391
- _ , _ , _ , err := t .RunCommand (cmd , 0 )
403
+ _ , _ , _ , err := t .RunCommand (cmd , 0 , true )
392
404
if err == nil {
393
405
t .canElevate = 1
394
406
return true // sudo password works
395
407
}
396
408
}
397
409
cmd := exec .Command ("sudo" , "-kS" , "ls" )
398
- _ , _ , _ , err := t .RunCommand (cmd , 0 )
410
+ _ , _ , _ , err := t .RunCommand (cmd , 0 , true )
399
411
if err == nil { // true - passwordless sudo works
400
412
t .canElevate = 1
401
413
return true
@@ -415,7 +427,7 @@ func (t *RemoteTarget) CanElevatePrivileges() bool {
415
427
return true
416
428
}
417
429
cmd := exec .Command ("sudo" , "-kS" , "ls" )
418
- _ , _ , _ , err := t .RunCommand (cmd , 0 )
430
+ _ , _ , _ , err := t .RunCommand (cmd , 0 , true )
419
431
if err == nil { // true - passwordless sudo works
420
432
t .canElevate = 1
421
433
return true
@@ -492,7 +504,7 @@ func (t *LocalTarget) GetUserPath() (string, error) {
492
504
func (t * RemoteTarget ) GetUserPath () (string , error ) {
493
505
if t .userPath == "" {
494
506
cmd := exec .Command ("echo" , "$PATH" )
495
- stdout , _ , _ , err := t .RunCommand (cmd , 0 )
507
+ stdout , _ , _ , err := t .RunCommand (cmd , 0 , true )
496
508
if err != nil {
497
509
return "" , err
498
510
}
@@ -594,7 +606,7 @@ func runLocalCommandWithInputWithTimeoutAsync(cmd *exec.Cmd, stdoutChannel chan
594
606
return nil
595
607
}
596
608
597
- func (t * RemoteTarget ) prepareSSHFlags (scp bool , async bool , prompt bool ) (flags []string ) {
609
+ func (t * RemoteTarget ) prepareSSHFlags (scp bool , useControlMaster bool , prompt bool ) (flags []string ) {
598
610
flags = []string {
599
611
"-2" ,
600
612
"-o" ,
@@ -621,7 +633,7 @@ func (t *RemoteTarget) prepareSSHFlags(scp bool, async bool, prompt bool) (flags
621
633
flags = append (flags , promptFlags ... )
622
634
}
623
635
// when using a control master, a long-running remote program doesn't get terminated when the local ssh client is terminated
624
- if ! async {
636
+ if useControlMaster {
625
637
controlPathFlags := []string {
626
638
"-o" ,
627
639
"ControlPath=" + filepath .Join (os .TempDir (), `control-%h-%p-%r` ),
@@ -654,10 +666,10 @@ func (t *RemoteTarget) prepareSSHFlags(scp bool, async bool, prompt bool) (flags
654
666
return
655
667
}
656
668
657
- func (t * RemoteTarget ) prepareSSHCommand (command []string , async bool , prompt bool ) []string {
669
+ func (t * RemoteTarget ) prepareSSHCommand (command []string , useControlMaster bool , prompt bool ) []string {
658
670
var cmd []string
659
671
cmd = append (cmd , "ssh" )
660
- cmd = append (cmd , t .prepareSSHFlags (false , async , prompt )... )
672
+ cmd = append (cmd , t .prepareSSHFlags (false , useControlMaster , prompt )... )
661
673
if t .user != "" {
662
674
cmd = append (cmd , t .user + "@" + t .host )
663
675
} else {
@@ -671,7 +683,7 @@ func (t *RemoteTarget) prepareSSHCommand(command []string, async bool, prompt bo
671
683
func (t * RemoteTarget ) prepareSCPCommand (src string , dstDir string , push bool ) []string {
672
684
var cmd []string
673
685
cmd = append (cmd , "scp" )
674
- cmd = append (cmd , t .prepareSSHFlags (true , false , false )... )
686
+ cmd = append (cmd , t .prepareSSHFlags (true , true , false )... )
675
687
if push {
676
688
fileInfo , err := os .Stat (src )
677
689
if err != nil {
@@ -698,11 +710,11 @@ func (t *RemoteTarget) prepareSCPCommand(src string, dstDir string, push bool) [
698
710
return cmd
699
711
}
700
712
701
- func (t * RemoteTarget ) prepareLocalCommand (cmd * exec.Cmd , async bool ) * exec.Cmd {
713
+ func (t * RemoteTarget ) prepareLocalCommand (cmd * exec.Cmd , useControlMaster bool ) * exec.Cmd {
702
714
var name string
703
715
var args []string
704
716
usePass := t .key == "" && t .sshPass != ""
705
- sshCommand := t .prepareSSHCommand (cmd .Args , async , usePass )
717
+ sshCommand := t .prepareSSHCommand (cmd .Args , useControlMaster , usePass )
706
718
if usePass {
707
719
name = t .sshpassPath
708
720
args = []string {"-e" , "--" }
@@ -741,7 +753,7 @@ func (t *RemoteTarget) prepareAndRunSCPCommand(srcPath string, dstDir string, is
741
753
742
754
func getArchitecture (t Target ) (arch string , err error ) {
743
755
cmd := exec .Command ("uname" , "-m" )
744
- arch , _ , _ , err = t .RunCommand (cmd , 0 )
756
+ arch , _ , _ , err = t .RunCommand (cmd , 0 , true )
745
757
if err != nil {
746
758
return
747
759
}
@@ -751,7 +763,7 @@ func getArchitecture(t Target) (arch string, err error) {
751
763
752
764
func getFamily (t Target ) (family string , err error ) {
753
765
cmd := exec .Command ("bash" , "-c" , "lscpu | grep -i \" ^CPU family:\" | awk '{print $NF}'" )
754
- family , _ , _ , err = t .RunCommand (cmd , 0 )
766
+ family , _ , _ , err = t .RunCommand (cmd , 0 , true )
755
767
if err != nil {
756
768
return
757
769
}
@@ -761,7 +773,7 @@ func getFamily(t Target) (family string, err error) {
761
773
762
774
func getModel (t Target ) (model string , err error ) {
763
775
cmd := exec .Command ("bash" , "-c" , "lscpu | grep -i model: | awk '{print $NF}'" )
764
- model , _ , _ , err = t .RunCommand (cmd , 0 )
776
+ model , _ , _ , err = t .RunCommand (cmd , 0 , true )
765
777
if err != nil {
766
778
return
767
779
}
@@ -776,7 +788,7 @@ func installLkms(t Target, lkms []string) (installedLkms []string, err error) {
776
788
}
777
789
for _ , lkm := range lkms {
778
790
slog .Debug ("attempting to install kernel module" , slog .String ("lkm" , lkm ))
779
- _ , _ , _ , err := t .RunCommand (exec .Command ("modprobe" , "--first-time" , lkm ), 10 )
791
+ _ , _ , _ , err := t .RunCommand (exec .Command ("modprobe" , "--first-time" , lkm ), 10 , true )
780
792
if err != nil {
781
793
slog .Debug ("kernel module already installed or problem installing" , slog .String ("lkm" , lkm ), slog .String ("error" , err .Error ()))
782
794
continue
@@ -794,7 +806,7 @@ func uninstallLkms(t Target, lkms []string) (err error) {
794
806
}
795
807
for _ , lkm := range lkms {
796
808
slog .Debug ("attempting to uninstall kernel module" , slog .String ("lkm" , lkm ))
797
- _ , _ , _ , err := t .RunCommand (exec .Command ("modprobe" , "-r" , lkm ), 10 )
809
+ _ , _ , _ , err := t .RunCommand (exec .Command ("modprobe" , "-r" , lkm ), 10 , true )
798
810
if err != nil {
799
811
slog .Error ("error uninstalling kernel module" , slog .String ("lkm" , lkm ), slog .String ("error" , err .Error ()))
800
812
continue
0 commit comments