github.com/yaling888/clash@v1.53.0/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"path/filepath"
     9  	"runtime"
    10  	"syscall"
    11  	_ "time/tzdata"
    12  
    13  	"github.com/phuslu/log"
    14  	"go.uber.org/automaxprocs/maxprocs"
    15  
    16  	"github.com/yaling888/clash/config"
    17  	C "github.com/yaling888/clash/constant"
    18  	"github.com/yaling888/clash/hub"
    19  	"github.com/yaling888/clash/hub/executor"
    20  	cLog "github.com/yaling888/clash/log"
    21  )
    22  
    23  var (
    24  	version            bool
    25  	testConfig         bool
    26  	homeDir            string
    27  	configFile         string
    28  	externalUI         string
    29  	externalController string
    30  	secret             string
    31  )
    32  
    33  func init() {
    34  	flag.StringVar(&homeDir, "d", os.Getenv("CLASH_HOME_DIR"), "set configuration directory")
    35  	flag.StringVar(&configFile, "f", os.Getenv("CLASH_CONFIG_FILE"), "specify configuration file")
    36  	flag.StringVar(&externalUI, "ext-ui", os.Getenv("CLASH_OVERRIDE_EXTERNAL_UI_DIR"),
    37  		"override external ui directory")
    38  	flag.StringVar(&externalController, "ext-ctl", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER"),
    39  		"override external controller address")
    40  	flag.StringVar(&secret, "secret", os.Getenv("CLASH_OVERRIDE_SECRET"),
    41  		"override secret for RESTful API")
    42  	flag.BoolVar(&version, "v", false, "show current version of clash")
    43  	flag.BoolVar(&testConfig, "t", false, "test configuration and exit")
    44  	flag.Parse()
    45  }
    46  
    47  func main() {
    48  	_, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {}))
    49  	if version {
    50  		fmt.Printf("Clash Plus Pro %s %s %s with %s %s\n",
    51  			C.Version,
    52  			runtime.GOOS,
    53  			runtime.GOARCH,
    54  			runtime.Version(),
    55  			C.BuildTime,
    56  		)
    57  		return
    58  	}
    59  
    60  	if homeDir != "" {
    61  		if !filepath.IsAbs(homeDir) {
    62  			currentDir, _ := os.Getwd()
    63  			homeDir = filepath.Join(currentDir, homeDir)
    64  		}
    65  		C.SetHomeDir(homeDir)
    66  	}
    67  
    68  	if configFile != "" {
    69  		if !filepath.IsAbs(configFile) {
    70  			currentDir, _ := os.Getwd()
    71  			configFile = filepath.Join(currentDir, configFile)
    72  		}
    73  		C.SetConfig(configFile)
    74  	} else {
    75  		configFile = filepath.Join(C.Path.HomeDir(), C.Path.Config())
    76  		C.SetConfig(configFile)
    77  	}
    78  
    79  	if err := config.Init(C.Path.HomeDir()); err != nil {
    80  		log.Fatal().
    81  			Err(err).
    82  			Str("dir", C.Path.HomeDir()).
    83  			Str("path", C.Path.Config()).
    84  			Msg("[Config] initial configuration failed")
    85  	}
    86  
    87  	if testConfig {
    88  		if _, err := executor.Parse(); err != nil {
    89  			log.Fatal().
    90  				Err(err).
    91  				Str("path", C.Path.Config()).
    92  				Msg("[Config] configuration file test failed")
    93  		}
    94  		log.Info().
    95  			Str("path", C.Path.Config()).
    96  			Msg("[Config] configuration file test is successful")
    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 secret != "" {
   108  		options = append(options, hub.WithSecret(secret))
   109  	}
   110  
   111  	if err := hub.Parse(options...); err != nil {
   112  		log.Fatal().
   113  			Err(err).
   114  			Str("path", C.Path.Config()).
   115  			Msg("[Config] parse config failed")
   116  	}
   117  
   118  	oldLevel := cLog.Level()
   119  	cLog.SetLevel(cLog.INFO)
   120  	log.Info().
   121  		Str("version", fmt.Sprintf("%s %s %s with %s %s",
   122  			C.Version,
   123  			runtime.GOOS,
   124  			runtime.GOARCH,
   125  			runtime.Version(),
   126  			C.BuildTime,
   127  		)).
   128  		Msg("[Main] Clash Plus started")
   129  	cLog.SetLevel(oldLevel)
   130  
   131  	defer executor.Shutdown()
   132  
   133  	sigCh := make(chan os.Signal, 1)
   134  	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
   135  	for {
   136  		s := <-sigCh
   137  		switch s {
   138  		case syscall.SIGHUP:
   139  			level := cLog.Level()
   140  			cLog.SetLevel(cLog.INFO)
   141  
   142  			log.Info().Str("path", C.Path.Config()).Msg("[Main] configuration file reloading...")
   143  
   144  			if conf, err := executor.Parse(); err == nil {
   145  				executor.ApplyConfig(conf, true)
   146  
   147  				level = cLog.Level()
   148  				cLog.SetLevel(cLog.INFO)
   149  
   150  				log.Info().Str("path", C.Path.Config()).Msg("[Main] configuration file reloaded")
   151  			} else {
   152  				log.Error().
   153  					Err(err).
   154  					Str("path", C.Path.Config()).
   155  					Msg("[Main] reload config failed")
   156  			}
   157  			cLog.SetLevel(level)
   158  		default:
   159  			return
   160  		}
   161  	}
   162  }