github.com/imannamdari/v2ray-core/v5@v5.0.5/main/commands/run.go (about)

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"log"
     7  	"os"
     8  	"os/signal"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strings"
    12  	"syscall"
    13  
    14  	core "github.com/imannamdari/v2ray-core/v5"
    15  	"github.com/imannamdari/v2ray-core/v5/common/cmdarg"
    16  	"github.com/imannamdari/v2ray-core/v5/common/platform"
    17  	"github.com/imannamdari/v2ray-core/v5/main/commands/base"
    18  )
    19  
    20  // CmdRun runs V2Ray with config
    21  var CmdRun = &base.Command{
    22  	CustomFlags: true,
    23  	UsageLine:   "{{.Exec}} run [-c config.json] [-d dir]",
    24  	Short:       "run V2Ray with config",
    25  	Long: `
    26  Run V2Ray with config.
    27  
    28  {{.Exec}} will also use the config directory specified by environment 
    29  variable "v2ray.location.confdir". If no config found, it tries 
    30  to load config from one of below:
    31  
    32  	1. The default "config.json" in the current directory
    33  	2. The config file from ENV "v2ray.location.config"
    34  	3. The stdin if all failed above
    35  
    36  Arguments:
    37  
    38  	-c, -config <file>
    39  		Config file for V2Ray. Multiple assign is accepted.
    40  
    41  	-d, -confdir <dir>
    42  		A directory with config files. Multiple assign is accepted.
    43  
    44  	-r
    45  		Load confdir recursively.
    46  
    47  	-format <format>
    48  		Format of config input. (default "auto")
    49  
    50  Examples:
    51  
    52  	{{.Exec}} {{.LongName}} -c config.json
    53  	{{.Exec}} {{.LongName}} -d path/to/dir
    54  
    55  Use "{{.Exec}} help format-loader" for more information about format.
    56  	`,
    57  	Run: executeRun,
    58  }
    59  
    60  var (
    61  	configFiles          cmdarg.Arg
    62  	configDirs           cmdarg.Arg
    63  	configFormat         *string
    64  	configDirRecursively *bool
    65  )
    66  
    67  func setConfigFlags(cmd *base.Command) {
    68  	configFormat = cmd.Flag.String("format", core.FormatAuto, "")
    69  	configDirRecursively = cmd.Flag.Bool("r", false, "")
    70  
    71  	cmd.Flag.Var(&configFiles, "config", "")
    72  	cmd.Flag.Var(&configFiles, "c", "")
    73  	cmd.Flag.Var(&configDirs, "confdir", "")
    74  	cmd.Flag.Var(&configDirs, "d", "")
    75  }
    76  
    77  func executeRun(cmd *base.Command, args []string) {
    78  	setConfigFlags(cmd)
    79  	cmd.Flag.Parse(args)
    80  	printVersion()
    81  	configFiles = getConfigFilePath()
    82  	server, err := startV2Ray()
    83  	if err != nil {
    84  		base.Fatalf("Failed to start: %s", err)
    85  	}
    86  
    87  	if err := server.Start(); err != nil {
    88  		base.Fatalf("Failed to start: %s", err)
    89  	}
    90  	defer server.Close()
    91  
    92  	// Explicitly triggering GC to remove garbage from config loading.
    93  	runtime.GC()
    94  
    95  	{
    96  		osSignals := make(chan os.Signal, 1)
    97  		signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM)
    98  		<-osSignals
    99  	}
   100  }
   101  
   102  func fileExists(file string) bool {
   103  	info, err := os.Stat(file)
   104  	return err == nil && !info.IsDir()
   105  }
   106  
   107  func dirExists(file string) bool {
   108  	if file == "" {
   109  		return false
   110  	}
   111  	info, err := os.Stat(file)
   112  	return err == nil && info.IsDir()
   113  }
   114  
   115  func readConfDir(dirPath string, extension []string) cmdarg.Arg {
   116  	confs, err := ioutil.ReadDir(dirPath)
   117  	if err != nil {
   118  		base.Fatalf("failed to read dir %s: %s", dirPath, err)
   119  	}
   120  	files := make(cmdarg.Arg, 0)
   121  	for _, f := range confs {
   122  		ext := filepath.Ext(f.Name())
   123  		for _, e := range extension {
   124  			if strings.EqualFold(e, ext) {
   125  				files.Set(filepath.Join(dirPath, f.Name()))
   126  				break
   127  			}
   128  		}
   129  	}
   130  	return files
   131  }
   132  
   133  // getFolderFiles get files in the folder and it's children
   134  func readConfDirRecursively(dirPath string, extension []string) cmdarg.Arg {
   135  	files := make(cmdarg.Arg, 0)
   136  	err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
   137  		ext := filepath.Ext(path)
   138  		for _, e := range extension {
   139  			if strings.EqualFold(e, ext) {
   140  				files.Set(path)
   141  				break
   142  			}
   143  		}
   144  		return nil
   145  	})
   146  	if err != nil {
   147  		base.Fatalf("failed to read dir %s: %s", dirPath, err)
   148  	}
   149  	return files
   150  }
   151  
   152  func getConfigFilePath() cmdarg.Arg {
   153  	extension, err := core.GetLoaderExtensions(*configFormat)
   154  	if err != nil {
   155  		base.Fatalf(err.Error())
   156  	}
   157  	dirReader := readConfDir
   158  	if *configDirRecursively {
   159  		dirReader = readConfDirRecursively
   160  	}
   161  	if len(configDirs) > 0 {
   162  		for _, d := range configDirs {
   163  			log.Println("Using confdir from arg:", d)
   164  			configFiles = append(configFiles, dirReader(d, extension)...)
   165  		}
   166  	} else if envConfDir := platform.GetConfDirPath(); dirExists(envConfDir) {
   167  		log.Println("Using confdir from env:", envConfDir)
   168  		configFiles = append(configFiles, dirReader(envConfDir, extension)...)
   169  	}
   170  	if len(configFiles) > 0 {
   171  		return configFiles
   172  	}
   173  
   174  	if len(configFiles) == 0 && len(configDirs) > 0 {
   175  		base.Fatalf("no config file found with extension: %s", extension)
   176  	}
   177  
   178  	if workingDir, err := os.Getwd(); err == nil {
   179  		configFile := filepath.Join(workingDir, "config.json")
   180  		if fileExists(configFile) {
   181  			log.Println("Using default config: ", configFile)
   182  			return cmdarg.Arg{configFile}
   183  		}
   184  	}
   185  
   186  	if configFile := platform.GetConfigurationPath(); fileExists(configFile) {
   187  		log.Println("Using config from env: ", configFile)
   188  		return cmdarg.Arg{configFile}
   189  	}
   190  
   191  	return nil
   192  }
   193  
   194  func startV2Ray() (core.Server, error) {
   195  	config, err := core.LoadConfig(*configFormat, configFiles)
   196  	if err != nil {
   197  		if len(configFiles) == 0 {
   198  			err = newError("failed to load config").Base(err)
   199  		} else {
   200  			err = newError(fmt.Sprintf("failed to load config: %s", configFiles)).Base(err)
   201  		}
   202  		return nil, err
   203  	}
   204  
   205  	server, err := core.New(config)
   206  	if err != nil {
   207  		return nil, newError("failed to create server").Base(err)
   208  	}
   209  
   210  	return server, nil
   211  }