github.com/sagernet/sing-box@v1.9.0-rc.20/box_outbound.go (about)

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