github.com/kelleygo/clashcore@v1.0.2/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  	"sync"
    12  	"syscall"
    13  	"time"
    14  
    15  	"github.com/kelleygo/clashcore/config"
    16  	C "github.com/kelleygo/clashcore/constant"
    17  	"github.com/kelleygo/clashcore/constant/features"
    18  	"github.com/kelleygo/clashcore/hub"
    19  	"github.com/kelleygo/clashcore/hub/executor"
    20  	"github.com/kelleygo/clashcore/log"
    21  
    22  	"go.uber.org/automaxprocs/maxprocs"
    23  )
    24  
    25  var (
    26  	version                bool
    27  	testConfig             bool
    28  	geodataMode            bool
    29  	homeDir                string
    30  	configFile             string
    31  	externalUI             string
    32  	externalController     string
    33  	externalControllerUnix string
    34  	secret                 string
    35  	updateGeoMux           sync.Mutex
    36  	updatingGeo            = false
    37  )
    38  
    39  func init() {
    40  	flag.StringVar(&homeDir, "d", os.Getenv("CLASH_HOME_DIR"), "set configuration directory")
    41  	flag.StringVar(&configFile, "f", os.Getenv("CLASH_CONFIG_FILE"), "specify configuration file")
    42  	flag.StringVar(&externalUI, "ext-ui", os.Getenv("CLASH_OVERRIDE_EXTERNAL_UI_DIR"), "override external ui directory")
    43  	flag.StringVar(&externalController, "ext-ctl", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER"), "override external controller address")
    44  	flag.StringVar(&externalControllerUnix, "ext-ctl-unix", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER_UNIX"), "override external controller unix address")
    45  	flag.StringVar(&secret, "secret", os.Getenv("CLASH_OVERRIDE_SECRET"), "override secret for RESTful API")
    46  	flag.BoolVar(&geodataMode, "m", false, "set geodata mode")
    47  	flag.BoolVar(&version, "v", false, "show current version of yiclashcore")
    48  	flag.BoolVar(&testConfig, "t", false, "test configuration and exit")
    49  	flag.Parse()
    50  }
    51  
    52  func main() {
    53  	_, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {}))
    54  	if version {
    55  		fmt.Printf("YiClashCore Meta %s %s %s with %s %s\n",
    56  			C.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime)
    57  		if tags := features.Tags(); len(tags) != 0 {
    58  			fmt.Printf("Use tags: %s\n", strings.Join(tags, ", "))
    59  		}
    60  
    61  		return
    62  	}
    63  
    64  	if homeDir != "" {
    65  		if !filepath.IsAbs(homeDir) {
    66  			currentDir, _ := os.Getwd()
    67  			homeDir = filepath.Join(currentDir, homeDir)
    68  		}
    69  		C.SetHomeDir(homeDir)
    70  	}
    71  
    72  	if configFile != "" {
    73  		if !filepath.IsAbs(configFile) {
    74  			currentDir, _ := os.Getwd()
    75  			configFile = filepath.Join(currentDir, configFile)
    76  		}
    77  	} else {
    78  		configFile = filepath.Join(C.Path.HomeDir(), C.Path.Config())
    79  	}
    80  	C.SetConfig(configFile)
    81  
    82  	if geodataMode {
    83  		C.GeodataMode = true
    84  	}
    85  
    86  	if err := config.Init(C.Path.HomeDir()); err != nil {
    87  		log.Fatalln("Initial configuration directory error: %s", err.Error())
    88  	}
    89  
    90  	if testConfig {
    91  		if _, err := executor.Parse(); err != nil {
    92  			log.Errorln(err.Error())
    93  			fmt.Printf("configuration file %s test failed\n", C.Path.Config())
    94  			os.Exit(1)
    95  		}
    96  		fmt.Printf("configuration file %s test is successful\n", C.Path.Config())
    97  		return
    98  	}
    99  
   100  	var options []hub.Option
   101  	if externalUI != "" {
   102  		options = append(options, hub.WithExternalUI(externalUI))
   103  	}
   104  	if externalController != "" {
   105  		options = append(options, hub.WithExternalController(externalController))
   106  	}
   107  	if externalControllerUnix != "" {
   108  		options = append(options, hub.WithExternalControllerUnix(externalControllerUnix))
   109  	}
   110  	if secret != "" {
   111  		options = append(options, hub.WithSecret(secret))
   112  	}
   113  
   114  	if err := hub.Parse(options...); err != nil {
   115  		log.Fatalln("Parse config error: %s", err.Error())
   116  	}
   117  
   118  	if C.GeoAutoUpdate {
   119  		ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour)
   120  
   121  		log.Infoln("[GEO] Start update GEO database every %d hours", C.GeoUpdateInterval)
   122  		go func() {
   123  			for range ticker.C {
   124  				updateGeoDatabases()
   125  			}
   126  		}()
   127  	}
   128  
   129  	defer executor.Shutdown()
   130  
   131  	termSign := make(chan os.Signal, 1)
   132  	hupSign := make(chan os.Signal, 1)
   133  	signal.Notify(termSign, syscall.SIGINT, syscall.SIGTERM)
   134  	signal.Notify(hupSign, syscall.SIGHUP)
   135  	for {
   136  		select {
   137  		case <-termSign:
   138  			return
   139  		case <-hupSign:
   140  			if cfg, err := executor.ParseWithPath(C.Path.Config()); err == nil {
   141  				executor.ApplyConfig(cfg, true)
   142  			} else {
   143  				log.Errorln("Parse config error: %s", err.Error())
   144  			}
   145  		}
   146  	}
   147  }
   148  
   149  func updateGeoDatabases() {
   150  	log.Infoln("[GEO] Start updating GEO database")
   151  	updateGeoMux.Lock()
   152  
   153  	if updatingGeo {
   154  		updateGeoMux.Unlock()
   155  		log.Infoln("[GEO] GEO database is updating, skip")
   156  		return
   157  	}
   158  
   159  	updatingGeo = true
   160  	updateGeoMux.Unlock()
   161  
   162  	go func() {
   163  		defer func() {
   164  			updatingGeo = false
   165  		}()
   166  
   167  		log.Infoln("[GEO] Updating GEO database")
   168  
   169  		if err := config.UpdateGeoDatabases(); err != nil {
   170  			log.Errorln("[GEO] update GEO database error: %s", err.Error())
   171  			return
   172  		}
   173  
   174  		cfg, err := executor.ParseWithPath(C.Path.Config())
   175  		if err != nil {
   176  			log.Errorln("[GEO] update GEO database failed: %s", err.Error())
   177  			return
   178  		}
   179  
   180  		log.Infoln("[GEO] Update GEO database success, apply new config")
   181  		executor.ApplyConfig(cfg, false)
   182  	}()
   183  }