box_outbound.go 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  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.StartTimeout)
  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.(interface {
  46. Start() error
  47. }); isStarter {
  48. monitor.Start("initialize outbound/", outboundToStart.Type(), "[", outboundTag, "]")
  49. err := starter.Start()
  50. monitor.Finish()
  51. if err != nil {
  52. return E.Cause(err, "initialize outbound/", outboundToStart.Type(), "[", outboundTag, "]")
  53. }
  54. }
  55. }
  56. if len(started) == len(s.outbounds) {
  57. break
  58. }
  59. if canContinue {
  60. continue
  61. }
  62. currentOutbound := common.Find(s.outbounds, func(it adapter.Outbound) bool {
  63. return !started[outboundTags[it]]
  64. })
  65. var lintOutbound func(oTree []string, oCurrent adapter.Outbound) error
  66. lintOutbound = func(oTree []string, oCurrent adapter.Outbound) error {
  67. problemOutboundTag := common.Find(oCurrent.Dependencies(), func(it string) bool {
  68. return !started[it]
  69. })
  70. if common.Contains(oTree, problemOutboundTag) {
  71. return E.New("circular outbound dependency: ", strings.Join(oTree, " -> "), " -> ", problemOutboundTag)
  72. }
  73. problemOutbound := outbounds[problemOutboundTag]
  74. if problemOutbound == nil {
  75. return E.New("dependency[", problemOutboundTag, "] not found for outbound[", outboundTags[oCurrent], "]")
  76. }
  77. return lintOutbound(append(oTree, problemOutboundTag), problemOutbound)
  78. }
  79. return lintOutbound([]string{outboundTags[currentOutbound]}, currentOutbound)
  80. }
  81. return nil
  82. }