exec.go 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. package exe
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "os/exec"
  7. "strings"
  8. "syscall"
  9. "time"
  10. "github.com/Jguer/yay/v10/pkg/text"
  11. )
  12. type Runner interface {
  13. Capture(cmd *exec.Cmd, timeout int64) (stdout string, stderr string, err error)
  14. Show(cmd *exec.Cmd) error
  15. }
  16. type OSRunner struct{}
  17. func (r *OSRunner) Show(cmd *exec.Cmd) error {
  18. cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
  19. return cmd.Run()
  20. }
  21. func (r *OSRunner) Capture(cmd *exec.Cmd, timeout int64) (stdout, stderr string, err error) {
  22. var (
  23. outbuf, errbuf bytes.Buffer
  24. timer *time.Timer
  25. timedOut = false
  26. )
  27. cmd.Stdout = &outbuf
  28. cmd.Stderr = &errbuf
  29. cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
  30. err = cmd.Start()
  31. if err != nil {
  32. stdout = strings.TrimSpace(outbuf.String())
  33. stderr = strings.TrimSpace(errbuf.String())
  34. return stdout, stderr, err
  35. }
  36. if timeout != 0 {
  37. timer = time.AfterFunc(time.Duration(timeout)*time.Second, func() {
  38. err = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
  39. if err != nil {
  40. text.Errorln(err)
  41. }
  42. timedOut = true
  43. })
  44. }
  45. err = cmd.Wait()
  46. if timeout != 0 {
  47. timer.Stop()
  48. }
  49. stdout = strings.TrimSpace(outbuf.String())
  50. stderr = strings.TrimSpace(errbuf.String())
  51. if err != nil {
  52. return stdout, stderr, err
  53. }
  54. if timedOut {
  55. err = fmt.Errorf("command timed out")
  56. }
  57. return stdout, stderr, err
  58. }