github.com/v2fly/v2ray-core/v4@v4.45.2/main/main.go (about) 1 package main 2 3 //go:generate go run github.com/v2fly/v2ray-core/v4/common/errors/errorgen 4 5 import ( 6 "flag" 7 "fmt" 8 "log" 9 "os" 10 "os/signal" 11 "path" 12 "path/filepath" 13 "runtime" 14 "strings" 15 "syscall" 16 17 core "github.com/v2fly/v2ray-core/v4" 18 "github.com/v2fly/v2ray-core/v4/common/cmdarg" 19 "github.com/v2fly/v2ray-core/v4/common/platform" 20 _ "github.com/v2fly/v2ray-core/v4/main/distro/all" 21 ) 22 23 var ( 24 configFiles cmdarg.Arg // "Config file for V2Ray.", the option is customed type, parse in main 25 configDir string 26 version = flag.Bool("version", false, "Show current version of V2Ray.") 27 test = flag.Bool("test", false, "Test config file only, without launching V2Ray server.") 28 format = flag.String("format", "json", "Format of input file.") 29 30 /* We have to do this here because Golang's Test will also need to parse flag, before 31 * main func in this file is run. 32 */ 33 _ = func() error { // nolint: unparam 34 flag.Var(&configFiles, "config", "Config file for V2Ray. Multiple assign is accepted (only json). Latter ones overrides the former ones.") 35 flag.Var(&configFiles, "c", "Short alias of -config") 36 flag.StringVar(&configDir, "confdir", "", "A dir with multiple json config") 37 38 return nil 39 }() 40 ) 41 42 func fileExists(file string) bool { 43 info, err := os.Stat(file) 44 return err == nil && !info.IsDir() 45 } 46 47 func dirExists(file string) bool { 48 if file == "" { 49 return false 50 } 51 info, err := os.Stat(file) 52 return err == nil && info.IsDir() 53 } 54 55 func readConfDir(dirPath string) { 56 confs, err := os.ReadDir(dirPath) 57 if err != nil { 58 log.Fatalln(err) 59 } 60 for _, f := range confs { 61 if strings.HasSuffix(f.Name(), ".json") { 62 configFiles.Set(path.Join(dirPath, f.Name())) 63 } 64 } 65 } 66 67 func getConfigFilePath() cmdarg.Arg { 68 if dirExists(configDir) { 69 log.Println("Using confdir from arg:", configDir) 70 readConfDir(configDir) 71 } else if envConfDir := platform.GetConfDirPath(); dirExists(envConfDir) { 72 log.Println("Using confdir from env:", envConfDir) 73 readConfDir(envConfDir) 74 } 75 76 if len(configFiles) > 0 { 77 return configFiles 78 } 79 80 if workingDir, err := os.Getwd(); err == nil { 81 configFile := filepath.Join(workingDir, "config.json") 82 if fileExists(configFile) { 83 log.Println("Using default config: ", configFile) 84 return cmdarg.Arg{configFile} 85 } 86 } 87 88 if configFile := platform.GetConfigurationPath(); fileExists(configFile) { 89 log.Println("Using config from env: ", configFile) 90 return cmdarg.Arg{configFile} 91 } 92 93 log.Println("Using config from STDIN") 94 return cmdarg.Arg{"stdin:"} 95 } 96 97 func GetConfigFormat() string { 98 switch strings.ToLower(*format) { 99 case "pb", "protobuf": 100 return "protobuf" 101 default: 102 return "json" 103 } 104 } 105 106 func startV2Ray() (core.Server, error) { 107 configFiles := getConfigFilePath() 108 109 config, err := core.LoadConfig(GetConfigFormat(), configFiles[0], configFiles) 110 if err != nil { 111 return nil, newError("failed to read config files: [", configFiles.String(), "]").Base(err) 112 } 113 114 server, err := core.New(config) 115 if err != nil { 116 return nil, newError("failed to create server").Base(err) 117 } 118 119 return server, nil 120 } 121 122 func printVersion() { 123 version := core.VersionStatement() 124 for _, s := range version { 125 fmt.Println(s) 126 } 127 } 128 129 func main() { 130 flag.Parse() 131 132 printVersion() 133 134 if *version { 135 return 136 } 137 138 server, err := startV2Ray() 139 if err != nil { 140 fmt.Println(err) 141 // Configuration error. Exit with a special value to prevent systemd from restarting. 142 os.Exit(23) 143 } 144 145 if *test { 146 fmt.Println("Configuration OK.") 147 os.Exit(0) 148 } 149 150 if err := server.Start(); err != nil { 151 fmt.Println("Failed to start", err) 152 os.Exit(-1) 153 } 154 defer server.Close() 155 156 // Explicitly triggering GC to remove garbage from config loading. 157 runtime.GC() 158 159 { 160 osSignals := make(chan os.Signal, 1) 161 signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM) 162 <-osSignals 163 } 164 }