box_outbound.go 2.4 KB

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