api.go 3.7 KB

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