package cmd import ( "fmt" "log" "os" "os/signal" "path" "runtime" "strings" "syscall" "time" "github.com/fsnotify/fsnotify" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/XrayR-project/XrayR/panel" ) var ( cfgFile string rootCmd = &cobra.Command{ Use: "XrayR", Run: func(cmd *cobra.Command, args []string) { if err := run(); err != nil { log.Fatal(err) } }, } ) func init() { rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "Config file for XrayR.") } func getConfig() *viper.Viper { config := viper.New() // Set custom path and name if cfgFile != "" { configName := path.Base(cfgFile) configFileExt := path.Ext(cfgFile) configNameOnly := strings.TrimSuffix(configName, configFileExt) configPath := path.Dir(cfgFile) config.SetConfigName(configNameOnly) config.SetConfigType(strings.TrimPrefix(configFileExt, ".")) config.AddConfigPath(configPath) // Set ASSET Path and Config Path for XrayR os.Setenv("XRAY_LOCATION_ASSET", configPath) os.Setenv("XRAY_LOCATION_CONFIG", configPath) } else { // Set default config path config.SetConfigName("config") config.SetConfigType("yml") config.AddConfigPath(".") } if err := config.ReadInConfig(); err != nil { log.Panicf("Config file error: %s \n", err) } config.WatchConfig() // Watch the config return config } func run() error { showVersion() config := getConfig() panelConfig := &panel.Config{} if err := config.Unmarshal(panelConfig); err != nil { return fmt.Errorf("Parse config file %v failed: %s \n", cfgFile, err) } p := panel.New(panelConfig) lastTime := time.Now() config.OnConfigChange(func(e fsnotify.Event) { // Discarding event received within a short period of time after receiving an event. if time.Now().After(lastTime.Add(3 * time.Second)) { // Hot reload function fmt.Println("Config file changed:", e.Name) p.Close() // Delete old instance and trigger GC runtime.GC() if err := config.Unmarshal(panelConfig); err != nil { log.Panicf("Parse config file %v failed: %s \n", cfgFile, err) } p.Start() lastTime = time.Now() } }) p.Start() defer p.Close() // Explicitly triggering GC to remove garbage from config loading. runtime.GC() // Running backend osSignals := make(chan os.Signal, 1) signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM) <-osSignals return nil } func Execute() error { return rootCmd.Execute() }