1
0

process.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package xray
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io/fs"
  9. "os"
  10. "os/exec"
  11. "runtime"
  12. "strings"
  13. "sync"
  14. "syscall"
  15. "x-ui/config"
  16. "x-ui/util/common"
  17. "github.com/Workiva/go-datastructures/queue"
  18. )
  19. func GetBinaryName() string {
  20. return fmt.Sprintf("xray-%s-%s", runtime.GOOS, runtime.GOARCH)
  21. }
  22. func GetBinaryPath() string {
  23. return config.GetBinFolderPath() + "/" + GetBinaryName()
  24. }
  25. func GetConfigPath() string {
  26. return config.GetBinFolderPath() + "/config.json"
  27. }
  28. func GetGeositePath() string {
  29. return config.GetBinFolderPath() + "/geosite.dat"
  30. }
  31. func GetGeoipPath() string {
  32. return config.GetBinFolderPath() + "/geoip.dat"
  33. }
  34. func GetIranPath() string {
  35. return config.GetBinFolderPath() + "/iran.dat"
  36. }
  37. func GetBlockedIPsPath() string {
  38. return config.GetBinFolderPath() + "/BlockedIps"
  39. }
  40. func stopProcess(p *Process) {
  41. p.Stop()
  42. }
  43. type Process struct {
  44. *process
  45. }
  46. func NewProcess(xrayConfig *Config) *Process {
  47. p := &Process{newProcess(xrayConfig)}
  48. runtime.SetFinalizer(p, stopProcess)
  49. return p
  50. }
  51. type process struct {
  52. cmd *exec.Cmd
  53. version string
  54. apiPort int
  55. config *Config
  56. lines *queue.Queue
  57. exitErr error
  58. }
  59. func newProcess(config *Config) *process {
  60. return &process{
  61. version: "Unknown",
  62. config: config,
  63. lines: queue.New(100),
  64. }
  65. }
  66. func (p *process) IsRunning() bool {
  67. if p.cmd == nil || p.cmd.Process == nil {
  68. return false
  69. }
  70. if p.cmd.ProcessState == nil {
  71. return true
  72. }
  73. return false
  74. }
  75. func (p *process) GetErr() error {
  76. return p.exitErr
  77. }
  78. func (p *process) GetResult() string {
  79. if p.lines.Empty() && p.exitErr != nil {
  80. return p.exitErr.Error()
  81. }
  82. items, _ := p.lines.TakeUntil(func(item interface{}) bool {
  83. return true
  84. })
  85. lines := make([]string, 0, len(items))
  86. for _, item := range items {
  87. lines = append(lines, item.(string))
  88. }
  89. return strings.Join(lines, "\n")
  90. }
  91. func (p *process) GetVersion() string {
  92. return p.version
  93. }
  94. func (p *Process) GetAPIPort() int {
  95. return p.apiPort
  96. }
  97. func (p *Process) GetConfig() *Config {
  98. return p.config
  99. }
  100. func (p *process) refreshAPIPort() {
  101. for _, inbound := range p.config.InboundConfigs {
  102. if inbound.Tag == "api" {
  103. p.apiPort = inbound.Port
  104. break
  105. }
  106. }
  107. }
  108. func (p *process) refreshVersion() {
  109. cmd := exec.Command(GetBinaryPath(), "-version")
  110. data, err := cmd.Output()
  111. if err != nil {
  112. p.version = "Unknown"
  113. } else {
  114. datas := bytes.Split(data, []byte(" "))
  115. if len(datas) <= 1 {
  116. p.version = "Unknown"
  117. } else {
  118. p.version = string(datas[1])
  119. }
  120. }
  121. }
  122. func (p *process) Start() (err error) {
  123. if p.IsRunning() {
  124. return errors.New("xray is already running")
  125. }
  126. defer func() {
  127. if err != nil {
  128. p.exitErr = err
  129. }
  130. }()
  131. data, err := json.MarshalIndent(p.config, "", " ")
  132. if err != nil {
  133. return common.NewErrorf("Failed to generate xray configuration file: %v", err)
  134. }
  135. configPath := GetConfigPath()
  136. err = os.WriteFile(configPath, data, fs.ModePerm)
  137. if err != nil {
  138. return common.NewErrorf("Failed to write configuration file: %v", err)
  139. }
  140. cmd := exec.Command(GetBinaryPath(), "-c", configPath, "-restrictedIPsPath", GetBlockedIPsPath())
  141. p.cmd = cmd
  142. stdReader, err := cmd.StdoutPipe()
  143. if err != nil {
  144. return err
  145. }
  146. errReader, err := cmd.StderrPipe()
  147. if err != nil {
  148. return err
  149. }
  150. var wg sync.WaitGroup
  151. wg.Add(2)
  152. go func() {
  153. defer wg.Done()
  154. reader := bufio.NewReaderSize(stdReader, 8192)
  155. for {
  156. line, _, err := reader.ReadLine()
  157. if err != nil {
  158. return
  159. }
  160. if p.lines.Len() >= 100 {
  161. p.lines.Get(1)
  162. }
  163. p.lines.Put(string(line))
  164. }
  165. }()
  166. go func() {
  167. defer wg.Done()
  168. reader := bufio.NewReaderSize(errReader, 8192)
  169. for {
  170. line, _, err := reader.ReadLine()
  171. if err != nil {
  172. return
  173. }
  174. if p.lines.Len() >= 100 {
  175. p.lines.Get(1)
  176. }
  177. p.lines.Put(string(line))
  178. }
  179. }()
  180. go func() {
  181. err := cmd.Run()
  182. if err != nil {
  183. p.exitErr = err
  184. }
  185. wg.Wait()
  186. }()
  187. p.refreshVersion()
  188. p.refreshAPIPort()
  189. return nil
  190. }
  191. func (p *process) Stop() error {
  192. if !p.IsRunning() {
  193. return errors.New("xray is not running")
  194. }
  195. return p.cmd.Process.Signal(syscall.SIGTERM)
  196. }