Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package process
  6. import (
  7. "bytes"
  8. "context"
  9. "errors"
  10. "fmt"
  11. "io"
  12. "os/exec"
  13. "sort"
  14. "sync"
  15. "time"
  16. )
  17. // TODO: This packages still uses a singleton for the Manager.
  18. // Once there's a decent web framework and dependencies are passed around like they should,
  19. // then we delete the singleton.
  20. var (
  21. // ErrExecTimeout represent a timeout error
  22. ErrExecTimeout = errors.New("Process execution timeout")
  23. manager *Manager
  24. // DefaultContext is the default context to run processing commands in
  25. DefaultContext = context.Background()
  26. )
  27. // Process represents a working process inheriting from Gitea.
  28. type Process struct {
  29. PID int64 // Process ID, not system one.
  30. Description string
  31. Start time.Time
  32. Cancel context.CancelFunc
  33. }
  34. // Manager knows about all processes and counts PIDs.
  35. type Manager struct {
  36. mutex sync.Mutex
  37. counter int64
  38. processes map[int64]*Process
  39. }
  40. // GetManager returns a Manager and initializes one as singleton if there's none yet
  41. func GetManager() *Manager {
  42. if manager == nil {
  43. manager = &Manager{
  44. processes: make(map[int64]*Process),
  45. }
  46. }
  47. return manager
  48. }
  49. // Add a process to the ProcessManager and returns its PID.
  50. func (pm *Manager) Add(description string, cancel context.CancelFunc) int64 {
  51. pm.mutex.Lock()
  52. pid := pm.counter + 1
  53. pm.processes[pid] = &Process{
  54. PID: pid,
  55. Description: description,
  56. Start: time.Now(),
  57. Cancel: cancel,
  58. }
  59. pm.counter = pid
  60. pm.mutex.Unlock()
  61. return pid
  62. }
  63. // Remove a process from the ProcessManager.
  64. func (pm *Manager) Remove(pid int64) {
  65. pm.mutex.Lock()
  66. delete(pm.processes, pid)
  67. pm.mutex.Unlock()
  68. }
  69. // Cancel a process in the ProcessManager.
  70. func (pm *Manager) Cancel(pid int64) {
  71. pm.mutex.Lock()
  72. process, ok := pm.processes[pid]
  73. pm.mutex.Unlock()
  74. if ok {
  75. process.Cancel()
  76. }
  77. }
  78. // Processes gets the processes in a thread safe manner
  79. func (pm *Manager) Processes() []*Process {
  80. pm.mutex.Lock()
  81. processes := make([]*Process, 0, len(pm.processes))
  82. for _, process := range pm.processes {
  83. processes = append(processes, process)
  84. }
  85. pm.mutex.Unlock()
  86. sort.Sort(processList(processes))
  87. return processes
  88. }
  89. // Exec a command and use the default timeout.
  90. func (pm *Manager) Exec(desc, cmdName string, args ...string) (string, string, error) {
  91. return pm.ExecDir(-1, "", desc, cmdName, args...)
  92. }
  93. // ExecTimeout a command and use a specific timeout duration.
  94. func (pm *Manager) ExecTimeout(timeout time.Duration, desc, cmdName string, args ...string) (string, string, error) {
  95. return pm.ExecDir(timeout, "", desc, cmdName, args...)
  96. }
  97. // ExecDir a command and use the default timeout.
  98. func (pm *Manager) ExecDir(timeout time.Duration, dir, desc, cmdName string, args ...string) (string, string, error) {
  99. return pm.ExecDirEnv(timeout, dir, desc, nil, cmdName, args...)
  100. }
  101. // ExecDirEnv runs a command in given path and environment variables, and waits for its completion
  102. // up to the given timeout (or DefaultTimeout if -1 is given).
  103. // Returns its complete stdout and stderr
  104. // outputs and an error, if any (including timeout)
  105. func (pm *Manager) ExecDirEnv(timeout time.Duration, dir, desc string, env []string, cmdName string, args ...string) (string, string, error) {
  106. return pm.ExecDirEnvStdIn(timeout, dir, desc, env, nil, cmdName, args...)
  107. }
  108. // ExecDirEnvStdIn runs a command in given path and environment variables with provided stdIN, and waits for its completion
  109. // up to the given timeout (or DefaultTimeout if -1 is given).
  110. // Returns its complete stdout and stderr
  111. // outputs and an error, if any (including timeout)
  112. func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env []string, stdIn io.Reader, cmdName string, args ...string) (string, string, error) {
  113. if timeout == -1 {
  114. timeout = 60 * time.Second
  115. }
  116. stdOut := new(bytes.Buffer)
  117. stdErr := new(bytes.Buffer)
  118. ctx, cancel := context.WithTimeout(DefaultContext, timeout)
  119. defer cancel()
  120. cmd := exec.CommandContext(ctx, cmdName, args...)
  121. cmd.Dir = dir
  122. cmd.Env = env
  123. cmd.Stdout = stdOut
  124. cmd.Stderr = stdErr
  125. if stdIn != nil {
  126. cmd.Stdin = stdIn
  127. }
  128. if err := cmd.Start(); err != nil {
  129. return "", "", err
  130. }
  131. pid := pm.Add(desc, cancel)
  132. err := cmd.Wait()
  133. pm.Remove(pid)
  134. if err != nil {
  135. err = fmt.Errorf("exec(%d:%s) failed: %v(%v) stdout: %v stderr: %v", pid, desc, err, ctx.Err(), stdOut, stdErr)
  136. }
  137. return stdOut.String(), stdErr.String(), err
  138. }
  139. type processList []*Process
  140. func (l processList) Len() int {
  141. return len(l)
  142. }
  143. func (l processList) Less(i, j int) bool {
  144. return l[i].PID < l[j].PID
  145. }
  146. func (l processList) Swap(i, j int) {
  147. l[i], l[j] = l[j], l[i]
  148. }