api.go 4.1 KB

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