box_outbound.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. package box
  2. import (
  3. "strings"
  4. "github.com/sagernet/sing-box/adapter"
  5. "github.com/sagernet/sing-box/common/taskmonitor"
  6. C "github.com/sagernet/sing-box/constant"
  7. "github.com/sagernet/sing/common"
  8. E "github.com/sagernet/sing/common/exceptions"
  9. F "github.com/sagernet/sing/common/format"
  10. )
  11. func (s *Box) startOutbounds() error {
  12. monitor := taskmonitor.New(s.logger, C.DefaultStartTimeout)
  13. outboundTags := make(map[adapter.Outbound]string)
  14. outbounds := make(map[string]adapter.Outbound)
  15. for i, outboundToStart := range s.outbounds {
  16. var outboundTag string
  17. if outboundToStart.Tag() == "" {
  18. outboundTag = F.ToString(i)
  19. } else {
  20. outboundTag = outboundToStart.Tag()
  21. }
  22. if _, exists := outbounds[outboundTag]; exists {
  23. return E.New("outbound tag ", outboundTag, " duplicated")
  24. }
  25. outboundTags[outboundToStart] = outboundTag
  26. outbounds[outboundTag] = outboundToStart
  27. }
  28. started := make(map[string]bool)
  29. for {
  30. canContinue := false
  31. startOne:
  32. for _, outboundToStart := range s.outbounds {
  33. outboundTag := outboundTags[outboundToStart]
  34. if started[outboundTag] {
  35. continue
  36. }
  37. dependencies := outboundToStart.Dependencies()
  38. for _, dependency := range dependencies {
  39. if !started[dependency] {
  40. continue startOne
  41. }
  42. }
  43. started[outboundTag] = true
  44. canContinue = true
  45. if starter, isStarter := outboundToStart.(common.Starter); isStarter {
  46. monitor.Start("initialize outbound/", outboundToStart.Type(), "[", outboundTag, "]")
  47. err := starter.Start()
  48. monitor.Finish()
  49. if err != nil {
  50. return E.Cause(err, "initialize outbound/", outboundToStart.Type(), "[", outboundTag, "]")
  51. }
  52. }
  53. }
  54. if len(started) == len(s.outbounds) {
  55. break
  56. }
  57. if canContinue {
  58. continue
  59. }
  60. currentOutbound := common.Find(s.outbounds, func(it adapter.Outbound) bool {
  61. return !started[outboundTags[it]]
  62. })
  63. var lintOutbound func(oTree []string, oCurrent adapter.Outbound) error
  64. lintOutbound = func(oTree []string, oCurrent adapter.Outbound) error {
  65. problemOutboundTag := common.Find(oCurrent.Dependencies(), func(it string) bool {
  66. return !started[it]
  67. })
  68. if common.Contains(oTree, problemOutboundTag) {
  69. return E.New("circular outbound dependency: ", strings.Join(oTree, " -> "), " -> ", problemOutboundTag)
  70. }
  71. problemOutbound := outbounds[problemOutboundTag]
  72. if problemOutbound == nil {
  73. return E.New("dependency[", problemOutboundTag, "] not found for outbound[", outboundTags[oCurrent], "]")
  74. }
  75. return lintOutbound(append(oTree, problemOutboundTag), problemOutbound)
  76. }
  77. return lintOutbound([]string{outboundTags[currentOutbound]}, currentOutbound)
  78. }
  79. return nil
  80. }