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.

104 lines
2.3 KiB

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