You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
2.2 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package process
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "os/exec"
  9. "time"
  10. )
  11. type Process struct {
  12. Label string
  13. ExecPath string
  14. Args []string
  15. Pid int
  16. StartTime time.Time
  17. EndTime time.Time
  18. OutputPath string
  19. Cmd *exec.Cmd `json:"-"`
  20. ExitState *os.ProcessState `json:"-"`
  21. OutputFile *os.File `json:"-"`
  22. WaitCh chan struct{} `json:"-"`
  23. }
  24. const (
  25. ProcessModeStd = iota
  26. ProcessModeDaemon
  27. )
  28. // execPath: command name
  29. // args: args to command. (should not include name)
  30. func Create(mode int, label string, execPath string, args []string, input string, outPath string) (*Process, error) {
  31. outFile, err := os.OpenFile(outPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
  32. if err != nil {
  33. return nil, err
  34. }
  35. cmd := exec.Command(execPath, args...)
  36. switch mode {
  37. case ProcessModeStd:
  38. cmd.Stdout = io.MultiWriter(os.Stdout, outFile)
  39. cmd.Stderr = io.MultiWriter(os.Stderr, outFile)
  40. cmd.Stdin = nil
  41. case ProcessModeDaemon:
  42. cmd.Stdout = outFile
  43. cmd.Stderr = outFile
  44. cmd.Stdin = nil
  45. }
  46. if input != "" {
  47. cmd.Stdin = bytes.NewReader([]byte(input))
  48. }
  49. if err := cmd.Start(); err != nil {
  50. return nil, err
  51. }
  52. proc := &Process{
  53. Label: label,
  54. ExecPath: execPath,
  55. Args: args,
  56. Pid: cmd.Process.Pid,
  57. StartTime: time.Now(),
  58. OutputPath: outPath,
  59. Cmd: cmd,
  60. ExitState: nil,
  61. OutputFile: outFile,
  62. WaitCh: make(chan struct{}),
  63. }
  64. go func() {
  65. err := proc.Cmd.Wait()
  66. if err != nil {
  67. fmt.Printf("Process exit: %v\n", err)
  68. if exitError, ok := err.(*exec.ExitError); ok {
  69. proc.ExitState = exitError.ProcessState
  70. }
  71. }
  72. proc.EndTime = time.Now() // TODO make this goroutine-safe
  73. close(proc.WaitCh)
  74. }()
  75. return proc, nil
  76. }
  77. func ReadOutput(proc *Process) string {
  78. output, err := ioutil.ReadFile(proc.OutputPath)
  79. if err != nil {
  80. return fmt.Sprintf("ERROR READING OUTPUT: %v", err)
  81. }
  82. return string(output)
  83. }
  84. func Stop(proc *Process, kill bool) error {
  85. defer proc.OutputFile.Close()
  86. if kill {
  87. fmt.Printf("Killing process %v\n", proc.Cmd.Process)
  88. return proc.Cmd.Process.Kill()
  89. } else {
  90. fmt.Printf("Stopping process %v\n", proc.Cmd.Process)
  91. return proc.Cmd.Process.Signal(os.Interrupt)
  92. }
  93. }