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.

136 lines
3.4 KiB

  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package process
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "os/exec"
  10. "time"
  11. "code.gitea.io/gitea/modules/log"
  12. )
  13. var (
  14. // ErrExecTimeout represent a timeout error
  15. ErrExecTimeout = errors.New("Process execution timeout")
  16. // DefaultTimeout is the timeout used by Exec* family
  17. // of function when timeout parameter is omitted or
  18. // passed as -1
  19. // NOTE: could be custom in config file for default.
  20. DefaultTimeout = 60 * time.Second
  21. )
  22. // Process represents a working process inherit from Gogs.
  23. type Process struct {
  24. Pid int64 // Process ID, not system one.
  25. Description string
  26. Start time.Time
  27. Cmd *exec.Cmd
  28. }
  29. // List of existing processes.
  30. var (
  31. curPid int64 = 1
  32. Processes []*Process
  33. )
  34. // Add adds a existing process and returns its PID.
  35. func Add(desc string, cmd *exec.Cmd) int64 {
  36. pid := curPid
  37. Processes = append(Processes, &Process{
  38. Pid: pid,
  39. Description: desc,
  40. Start: time.Now(),
  41. Cmd: cmd,
  42. })
  43. curPid++
  44. return pid
  45. }
  46. // ExecDir runs a command in given path and waits for its completion
  47. // up to the given timeout (or DefaultTimeout if -1 is given).
  48. // Returns its complete stdout and stderr
  49. // outputs and an error, if any (including timeout)
  50. func ExecDir(timeout time.Duration, dir, desc, cmdName string, args ...string) (string, string, error) {
  51. if timeout == -1 {
  52. timeout = DefaultTimeout
  53. }
  54. bufOut := new(bytes.Buffer)
  55. bufErr := new(bytes.Buffer)
  56. cmd := exec.Command(cmdName, args...)
  57. cmd.Dir = dir
  58. cmd.Stdout = bufOut
  59. cmd.Stderr = bufErr
  60. if err := cmd.Start(); err != nil {
  61. return "", err.Error(), err
  62. }
  63. pid := Add(desc, cmd)
  64. done := make(chan error)
  65. go func() {
  66. done <- cmd.Wait()
  67. }()
  68. var err error
  69. select {
  70. case <-time.After(timeout):
  71. if errKill := Kill(pid); errKill != nil {
  72. log.Error(4, "Exec(%d:%s): %v", pid, desc, errKill)
  73. }
  74. <-done
  75. return "", ErrExecTimeout.Error(), ErrExecTimeout
  76. case err = <-done:
  77. }
  78. Remove(pid)
  79. return bufOut.String(), bufErr.String(), err
  80. }
  81. // ExecTimeout runs a command and waits for its completion
  82. // up to the given timeout (or DefaultTimeout if -1 is given).
  83. // Returns its complete stdout and stderr
  84. // outputs and an error, if any (including timeout)
  85. func ExecTimeout(timeout time.Duration, desc, cmdName string, args ...string) (string, string, error) {
  86. return ExecDir(timeout, "", desc, cmdName, args...)
  87. }
  88. // Exec runs a command and waits for its completion
  89. // up to DefaultTimeout. Returns its complete stdout and stderr
  90. // outputs and an error, if any (including timeout)
  91. func Exec(desc, cmdName string, args ...string) (string, string, error) {
  92. return ExecDir(-1, "", desc, cmdName, args...)
  93. }
  94. // Remove removes a process from list.
  95. func Remove(pid int64) {
  96. for i, proc := range Processes {
  97. if proc.Pid == pid {
  98. Processes = append(Processes[:i], Processes[i+1:]...)
  99. return
  100. }
  101. }
  102. }
  103. // Kill kills and removes a process from list.
  104. func Kill(pid int64) error {
  105. for i, proc := range Processes {
  106. if proc.Pid == pid {
  107. if proc.Cmd != nil && proc.Cmd.Process != nil &&
  108. proc.Cmd.ProcessState != nil && !proc.Cmd.ProcessState.Exited() {
  109. if err := proc.Cmd.Process.Kill(); err != nil {
  110. return fmt.Errorf("fail to kill process(%d/%s): %v", proc.Pid, proc.Description, err)
  111. }
  112. }
  113. Processes = append(Processes[:i], Processes[i+1:]...)
  114. return nil
  115. }
  116. }
  117. return nil
  118. }