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.

103 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
  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. err = proc.OutputFile.Close()
  74. if err != nil {
  75. fmt.Printf("Error closing output file for %v: %v\n", proc.Label, err)
  76. }
  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. }