exec.go 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  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. }
  18. func (r *OSRunner) Show(cmd *exec.Cmd) error {
  19. cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
  20. return cmd.Run()
  21. }
  22. func (r *OSRunner) Capture(cmd *exec.Cmd, timeout int64) (stdout, stderr string, err error) {
  23. var outbuf, errbuf bytes.Buffer
  24. var timer *time.Timer
  25. timedOut := false
  26. cmd.Stdout = &outbuf
  27. cmd.Stderr = &errbuf
  28. cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
  29. err = cmd.Start()
  30. if err != nil {
  31. stdout = strings.TrimSpace(outbuf.String())
  32. stderr = strings.TrimSpace(errbuf.String())
  33. return stdout, stderr, err
  34. }
  35. if timeout != 0 {
  36. timer = time.AfterFunc(time.Duration(timeout)*time.Second, func() {
  37. err = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
  38. if err != nil {
  39. text.Errorln(err)
  40. }
  41. timedOut = true
  42. })
  43. }
  44. err = cmd.Wait()
  45. if timeout != 0 {
  46. timer.Stop()
  47. }
  48. stdout = strings.TrimSpace(outbuf.String())
  49. stderr = strings.TrimSpace(errbuf.String())
  50. if err != nil {
  51. return stdout, stderr, err
  52. }
  53. if timedOut {
  54. err = fmt.Errorf("command timed out")
  55. }
  56. return stdout, stderr, err
  57. }