cmd_run.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package main
  2. import (
  3. "context"
  4. "io"
  5. "os"
  6. "os/signal"
  7. "path/filepath"
  8. runtimeDebug "runtime/debug"
  9. "sort"
  10. "strings"
  11. "syscall"
  12. "time"
  13. "github.com/sagernet/sing-box"
  14. C "github.com/sagernet/sing-box/constant"
  15. "github.com/sagernet/sing-box/log"
  16. "github.com/sagernet/sing-box/option"
  17. E "github.com/sagernet/sing/common/exceptions"
  18. "github.com/sagernet/sing/common/json/badjson"
  19. "github.com/spf13/cobra"
  20. )
  21. var commandRun = &cobra.Command{
  22. Use: "run",
  23. Short: "Run service",
  24. Run: func(cmd *cobra.Command, args []string) {
  25. err := run()
  26. if err != nil {
  27. log.Fatal(err)
  28. }
  29. },
  30. }
  31. func init() {
  32. mainCommand.AddCommand(commandRun)
  33. }
  34. type OptionsEntry struct {
  35. content []byte
  36. path string
  37. options option.Options
  38. }
  39. func readConfigAt(path string) (*OptionsEntry, error) {
  40. var (
  41. configContent []byte
  42. err error
  43. )
  44. if path == "stdin" {
  45. configContent, err = io.ReadAll(os.Stdin)
  46. } else {
  47. configContent, err = os.ReadFile(path)
  48. }
  49. if err != nil {
  50. return nil, E.Cause(err, "read config at ", path)
  51. }
  52. var options option.Options
  53. err = options.UnmarshalJSON(configContent)
  54. if err != nil {
  55. return nil, E.Cause(err, "decode config at ", path)
  56. }
  57. return &OptionsEntry{
  58. content: configContent,
  59. path: path,
  60. options: options,
  61. }, nil
  62. }
  63. func readConfig() ([]*OptionsEntry, error) {
  64. var optionsList []*OptionsEntry
  65. for _, path := range configPaths {
  66. optionsEntry, err := readConfigAt(path)
  67. if err != nil {
  68. return nil, err
  69. }
  70. optionsList = append(optionsList, optionsEntry)
  71. }
  72. for _, directory := range configDirectories {
  73. entries, err := os.ReadDir(directory)
  74. if err != nil {
  75. return nil, E.Cause(err, "read config directory at ", directory)
  76. }
  77. for _, entry := range entries {
  78. if !strings.HasSuffix(entry.Name(), ".json") || entry.IsDir() {
  79. continue
  80. }
  81. optionsEntry, err := readConfigAt(filepath.Join(directory, entry.Name()))
  82. if err != nil {
  83. return nil, err
  84. }
  85. optionsList = append(optionsList, optionsEntry)
  86. }
  87. }
  88. sort.Slice(optionsList, func(i, j int) bool {
  89. return optionsList[i].path < optionsList[j].path
  90. })
  91. return optionsList, nil
  92. }
  93. func readConfigAndMerge() (option.Options, error) {
  94. optionsList, err := readConfig()
  95. if err != nil {
  96. return option.Options{}, err
  97. }
  98. if len(optionsList) == 1 {
  99. return optionsList[0].options, nil
  100. }
  101. var mergedOptions option.Options
  102. for _, options := range optionsList {
  103. mergedOptions, err = badjson.Merge(options.options, mergedOptions)
  104. if err != nil {
  105. return option.Options{}, E.Cause(err, "merge config at ", options.path)
  106. }
  107. }
  108. return mergedOptions, nil
  109. }
  110. func create() (*box.Box, context.CancelFunc, error) {
  111. options, err := readConfigAndMerge()
  112. if err != nil {
  113. return nil, nil, err
  114. }
  115. if disableColor {
  116. if options.Log == nil {
  117. options.Log = &option.LogOptions{}
  118. }
  119. options.Log.DisableColor = true
  120. }
  121. ctx, cancel := context.WithCancel(context.Background())
  122. instance, err := box.New(box.Options{
  123. Context: ctx,
  124. Options: options,
  125. })
  126. if err != nil {
  127. cancel()
  128. return nil, nil, E.Cause(err, "create service")
  129. }
  130. osSignals := make(chan os.Signal, 1)
  131. signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
  132. defer func() {
  133. signal.Stop(osSignals)
  134. close(osSignals)
  135. }()
  136. startCtx, finishStart := context.WithCancel(context.Background())
  137. go func() {
  138. _, loaded := <-osSignals
  139. if loaded {
  140. cancel()
  141. closeMonitor(startCtx)
  142. }
  143. }()
  144. err = instance.Start()
  145. finishStart()
  146. if err != nil {
  147. cancel()
  148. return nil, nil, E.Cause(err, "start service")
  149. }
  150. return instance, cancel, nil
  151. }
  152. func run() error {
  153. osSignals := make(chan os.Signal, 1)
  154. signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
  155. defer signal.Stop(osSignals)
  156. for {
  157. instance, cancel, err := create()
  158. if err != nil {
  159. return err
  160. }
  161. runtimeDebug.FreeOSMemory()
  162. for {
  163. osSignal := <-osSignals
  164. if osSignal == syscall.SIGHUP {
  165. err = check()
  166. if err != nil {
  167. log.Error(E.Cause(err, "reload service"))
  168. continue
  169. }
  170. }
  171. cancel()
  172. closeCtx, closed := context.WithCancel(context.Background())
  173. go closeMonitor(closeCtx)
  174. instance.Close()
  175. closed()
  176. if osSignal != syscall.SIGHUP {
  177. return nil
  178. }
  179. break
  180. }
  181. }
  182. }
  183. func closeMonitor(ctx context.Context) {
  184. time.Sleep(C.DefaultStopFatalTimeout)
  185. select {
  186. case <-ctx.Done():
  187. return
  188. default:
  189. }
  190. log.Fatal("sing-box did not close!")
  191. }