github.com/v2fly/v2ray-core/v4@v4.45.2/infra/control/api.go (about) 1 package control 2 3 import ( 4 "context" 5 "errors" 6 "flag" 7 "fmt" 8 "strings" 9 "time" 10 11 "github.com/golang/protobuf/proto" 12 "google.golang.org/grpc" 13 14 logService "github.com/v2fly/v2ray-core/v4/app/log/command" 15 statsService "github.com/v2fly/v2ray-core/v4/app/stats/command" 16 "github.com/v2fly/v2ray-core/v4/common" 17 ) 18 19 type APICommand struct{} 20 21 func (c *APICommand) Name() string { 22 return "api" 23 } 24 25 func (c *APICommand) Description() Description { 26 return Description{ 27 Short: "Call V2Ray API", 28 Usage: []string{ 29 "v2ctl api [--server=127.0.0.1:8080] Service.Method Request", 30 "Call an API in an V2Ray process.", 31 "The following methods are currently supported:", 32 "\tLoggerService.RestartLogger", 33 "\tStatsService.GetStats", 34 "\tStatsService.QueryStats", 35 "API calls in this command have a timeout to the server of 3 seconds.", 36 "Examples:", 37 "v2ctl api --server=127.0.0.1:8080 LoggerService.RestartLogger '' ", 38 "v2ctl api --server=127.0.0.1:8080 StatsService.QueryStats 'pattern: \"\" reset: false'", 39 "v2ctl api --server=127.0.0.1:8080 StatsService.GetStats 'name: \"inbound>>>statin>>>traffic>>>downlink\" reset: false'", 40 "v2ctl api --server=127.0.0.1:8080 StatsService.GetSysStats ''", 41 }, 42 } 43 } 44 45 func (c *APICommand) Execute(args []string) error { 46 fs := flag.NewFlagSet(c.Name(), flag.ContinueOnError) 47 48 serverAddrPtr := fs.String("server", "127.0.0.1:8080", "Server address") 49 50 if err := fs.Parse(args); err != nil { 51 return err 52 } 53 54 unnamedArgs := fs.Args() 55 if len(unnamedArgs) < 2 { 56 return newError("service name or request not specified.") 57 } 58 59 service, method := getServiceMethod(unnamedArgs[0]) 60 handler, found := serivceHandlerMap[strings.ToLower(service)] 61 if !found { 62 return newError("unknown service: ", service) 63 } 64 65 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 66 defer cancel() 67 68 conn, err := grpc.DialContext(ctx, *serverAddrPtr, grpc.WithInsecure(), grpc.WithBlock()) 69 if err != nil { 70 return newError("failed to dial ", *serverAddrPtr).Base(err) 71 } 72 defer conn.Close() 73 74 response, err := handler(ctx, conn, method, unnamedArgs[1]) 75 if err != nil { 76 return newError("failed to call service ", unnamedArgs[0]).Base(err) 77 } 78 79 fmt.Println(response) 80 return nil 81 } 82 83 func getServiceMethod(s string) (string, string) { 84 ss := strings.Split(s, ".") 85 service := ss[0] 86 var method string 87 if len(ss) > 1 { 88 method = ss[1] 89 } 90 return service, method 91 } 92 93 type serviceHandler func(ctx context.Context, conn *grpc.ClientConn, method string, request string) (string, error) 94 95 var serivceHandlerMap = map[string]serviceHandler{ 96 "statsservice": callStatsService, 97 "loggerservice": callLogService, 98 } 99 100 func callLogService(ctx context.Context, conn *grpc.ClientConn, method string, request string) (string, error) { 101 client := logService.NewLoggerServiceClient(conn) 102 103 switch strings.ToLower(method) { 104 case "restartlogger": 105 r := &logService.RestartLoggerRequest{} 106 if err := proto.UnmarshalText(request, r); err != nil { 107 return "", err 108 } 109 resp, err := client.RestartLogger(ctx, r) 110 if err != nil { 111 return "", err 112 } 113 return proto.MarshalTextString(resp), nil 114 default: 115 return "", errors.New("Unknown method: " + method) 116 } 117 } 118 119 func callStatsService(ctx context.Context, conn *grpc.ClientConn, method string, request string) (string, error) { 120 client := statsService.NewStatsServiceClient(conn) 121 122 switch strings.ToLower(method) { 123 case "getstats": 124 r := &statsService.GetStatsRequest{} 125 if err := proto.UnmarshalText(request, r); err != nil { 126 return "", err 127 } 128 resp, err := client.GetStats(ctx, r) 129 if err != nil { 130 return "", err 131 } 132 return proto.MarshalTextString(resp), nil 133 case "querystats": 134 r := &statsService.QueryStatsRequest{} 135 if err := proto.UnmarshalText(request, r); err != nil { 136 return "", err 137 } 138 resp, err := client.QueryStats(ctx, r) 139 if err != nil { 140 return "", err 141 } 142 return proto.MarshalTextString(resp), nil 143 case "getsysstats": 144 // SysStatsRequest is an empty message 145 r := &statsService.SysStatsRequest{} 146 resp, err := client.GetSysStats(ctx, r) 147 if err != nil { 148 return "", err 149 } 150 return proto.MarshalTextString(resp), nil 151 default: 152 return "", errors.New("Unknown method: " + method) 153 } 154 } 155 156 func init() { 157 common.Must(RegisterCommand(&APICommand{})) 158 }