LCOV - code coverage report
Current view: top level - lib/rpc - rpc.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 169 242 69.8 %
Date: 2024-12-02 08:25:18 Functions: 19 26 73.1 %

          Line data    Source code
       1             : /*   SPDX-License-Identifier: BSD-3-Clause
       2             :  *   Copyright (C) 2016 Intel Corporation. All rights reserved.
       3             :  *   Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved.
       4             :  *   Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
       5             :  */
       6             : 
       7             : #include <sys/file.h>
       8             : 
       9             : #include "spdk/stdinc.h"
      10             : 
      11             : #include "spdk/queue.h"
      12             : #include "spdk/rpc.h"
      13             : #include "spdk/env.h"
      14             : #include "spdk/log.h"
      15             : #include "spdk/string.h"
      16             : #include "spdk/util.h"
      17             : #include "spdk/version.h"
      18             : 
      19             : static uint32_t g_rpc_state = SPDK_RPC_STARTUP;
      20             : static bool g_rpcs_correct = true;
      21             : static char **g_rpcs_allowlist = NULL;
      22             : 
      23             : struct spdk_rpc_server {
      24             :         struct sockaddr_un listen_addr_unix;
      25             :         char lock_path[sizeof(((struct sockaddr_un *)0)->sun_path) + sizeof(".lock")];
      26             :         int lock_fd;
      27             :         struct spdk_jsonrpc_server *jsonrpc_server;
      28             : };
      29             : 
      30             : static struct spdk_rpc_server g_rpc_server;
      31             : 
      32             : struct spdk_rpc_method {
      33             :         const char *name;
      34             :         spdk_rpc_method_handler func;
      35             :         SLIST_ENTRY(spdk_rpc_method) slist;
      36             :         uint32_t state_mask;
      37             :         bool is_deprecated;
      38             :         struct spdk_rpc_method *is_alias_of;
      39             :         bool deprecation_warning_printed;
      40             : };
      41             : 
      42             : static SLIST_HEAD(, spdk_rpc_method) g_rpc_methods = SLIST_HEAD_INITIALIZER(g_rpc_methods);
      43             : 
      44             : void
      45           0 : spdk_rpc_set_state(uint32_t state)
      46             : {
      47           0 :         g_rpc_state = state;
      48           0 : }
      49             : 
      50             : uint32_t
      51           0 : spdk_rpc_get_state(void)
      52             : {
      53           0 :         return g_rpc_state;
      54             : }
      55             : 
      56             : static bool
      57           9 : rpc_is_allowed(const char *name)
      58             : {
      59             :         size_t i;
      60             : 
      61           9 :         if (g_rpcs_allowlist == NULL) {
      62           9 :                 return true;
      63             :         }
      64             : 
      65           0 :         for (i = 0; g_rpcs_allowlist[i] != NULL; i++) {
      66           0 :                 if (strcmp(name, g_rpcs_allowlist[i]) == 0) {
      67           0 :                         return true;
      68             :                 }
      69           0 :         }
      70             : 
      71           0 :         return false;
      72           9 : }
      73             : 
      74             : 
      75             : static struct spdk_rpc_method *
      76           6 : _get_rpc_method(const struct spdk_json_val *method)
      77             : {
      78             :         struct spdk_rpc_method *m;
      79             : 
      80           9 :         SLIST_FOREACH(m, &g_rpc_methods, slist) {
      81           6 :                 if (spdk_json_strequal(method, m->name)) {
      82           3 :                         if (!rpc_is_allowed(m->name)) {
      83           0 :                                 return NULL;
      84             :                         }
      85           3 :                         return m;
      86             :                 }
      87           3 :         }
      88             : 
      89           3 :         return NULL;
      90           6 : }
      91             : 
      92             : static struct spdk_rpc_method *
      93           2 : _get_rpc_method_raw(const char *method)
      94             : {
      95             :         struct spdk_json_val method_val;
      96             : 
      97           2 :         method_val.type = SPDK_JSON_VAL_STRING;
      98           2 :         method_val.len = strlen(method);
      99           2 :         method_val.start = (char *)method;
     100             : 
     101           2 :         return _get_rpc_method(&method_val);
     102             : }
     103             : 
     104             : static void
     105           4 : jsonrpc_handler(struct spdk_jsonrpc_request *request,
     106             :                 const struct spdk_json_val *method,
     107             :                 const struct spdk_json_val *params)
     108             : {
     109             :         struct spdk_rpc_method *m;
     110             : 
     111           4 :         assert(method != NULL);
     112             : 
     113           4 :         m = _get_rpc_method(method);
     114           4 :         if (m == NULL) {
     115           1 :                 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND, "Method not found");
     116           1 :                 return;
     117             :         }
     118             : 
     119           3 :         if (m->is_alias_of != NULL) {
     120           3 :                 if (m->is_deprecated && !m->deprecation_warning_printed) {
     121           1 :                         SPDK_WARNLOG("RPC method %s is deprecated.  Use %s instead.\n", m->name, m->is_alias_of->name);
     122           1 :                         m->deprecation_warning_printed = true;
     123           1 :                 }
     124           3 :                 m = m->is_alias_of;
     125           3 :         }
     126             : 
     127           3 :         if ((m->state_mask & g_rpc_state) == g_rpc_state) {
     128           1 :                 m->func(request, params);
     129           1 :         } else {
     130           2 :                 if (g_rpc_state == SPDK_RPC_STARTUP) {
     131           1 :                         spdk_jsonrpc_send_error_response_fmt(request,
     132             :                                                              SPDK_JSONRPC_ERROR_INVALID_STATE,
     133             :                                                              "Method may only be called after "
     134             :                                                              "framework is initialized "
     135             :                                                              "using framework_start_init RPC.");
     136           1 :                 } else {
     137           1 :                         spdk_jsonrpc_send_error_response_fmt(request,
     138             :                                                              SPDK_JSONRPC_ERROR_INVALID_STATE,
     139             :                                                              "Method may only be called before "
     140             :                                                              "framework is initialized. "
     141             :                                                              "Use --wait-for-rpc command line "
     142             :                                                              "parameter and then issue this RPC "
     143             :                                                              "before the framework_start_init RPC.");
     144             :                 }
     145             :         }
     146           4 : }
     147             : 
     148             : static int
     149           4 : _spdk_rpc_listen(const char *listen_addr, struct spdk_rpc_server *server)
     150             : {
     151             :         int rc;
     152             : 
     153           4 :         assert(listen_addr != NULL);
     154             : 
     155           4 :         server->listen_addr_unix.sun_family = AF_UNIX;
     156           8 :         rc = snprintf(server->listen_addr_unix.sun_path,
     157             :                       sizeof(server->listen_addr_unix.sun_path),
     158           4 :                       "%s", listen_addr);
     159           4 :         if (rc < 0 || (size_t)rc >= sizeof(server->listen_addr_unix.sun_path)) {
     160           0 :                 SPDK_ERRLOG("RPC Listen address Unix socket path too long\n");
     161           0 :                 return -1;
     162             :         }
     163             : 
     164           8 :         rc = snprintf(server->lock_path, sizeof(server->lock_path), "%s.lock",
     165           4 :                       server->listen_addr_unix.sun_path);
     166           4 :         if (rc < 0 || (size_t)rc >= sizeof(server->lock_path)) {
     167           0 :                 SPDK_ERRLOG("RPC lock path too long\n");
     168           0 :                 return -1;
     169             :         }
     170             : 
     171           4 :         server->lock_fd = open(server->lock_path, O_RDWR | O_CREAT, 0600);
     172           4 :         if (server->lock_fd == -1) {
     173           0 :                 SPDK_ERRLOG("Cannot open lock file %s: %s\n",
     174             :                             server->lock_path, spdk_strerror(errno));
     175           0 :                 return -1;
     176             :         }
     177             : 
     178           4 :         rc = flock(server->lock_fd, LOCK_EX | LOCK_NB);
     179           4 :         if (rc != 0) {
     180           0 :                 SPDK_ERRLOG("RPC Unix domain socket path %s in use. Specify another.\n",
     181             :                             server->listen_addr_unix.sun_path);
     182           0 :                 return -1;
     183             :         }
     184             : 
     185             :         /*
     186             :          * Since we acquired the lock, it is safe to delete the Unix socket file
     187             :          * if it still exists from a previous process.
     188             :          */
     189           4 :         unlink(server->listen_addr_unix.sun_path);
     190             : 
     191           4 :         server->jsonrpc_server = spdk_jsonrpc_server_listen(AF_UNIX, 0,
     192           4 :                                  (struct sockaddr *) & server->listen_addr_unix,
     193             :                                  sizeof(server->listen_addr_unix),
     194             :                                  jsonrpc_handler);
     195           4 :         if (server->jsonrpc_server == NULL) {
     196           0 :                 SPDK_ERRLOG("spdk_jsonrpc_server_listen() failed\n");
     197           0 :                 close(server->lock_fd);
     198           0 :                 unlink(server->lock_path);
     199           0 :                 return -1;
     200             :         }
     201             : 
     202           4 :         return 0;
     203           4 : }
     204             : 
     205           1 : SPDK_LOG_DEPRECATION_REGISTER(spdk_rpc_listen, "spdk_rpc_listen is deprecated", "v24.09", 0);
     206             : 
     207             : int
     208           1 : spdk_rpc_listen(const char *listen_addr)
     209             : {
     210             :         struct spdk_rpc_server *server;
     211             :         int rc;
     212             : 
     213           1 :         SPDK_LOG_DEPRECATED(spdk_rpc_listen);
     214             : 
     215           1 :         memset(&g_rpc_server.listen_addr_unix, 0, sizeof(g_rpc_server.listen_addr_unix));
     216           1 :         server = &g_rpc_server;
     217             : 
     218           1 :         rc = _spdk_rpc_listen(listen_addr, server);
     219           1 :         if (rc) {
     220           0 :                 server->listen_addr_unix.sun_path[0] = '\0';
     221           0 :                 server->lock_path[0] = '\0';
     222           0 :         }
     223             : 
     224           1 :         return rc;
     225             : }
     226             : 
     227             : struct spdk_rpc_server *
     228           3 : spdk_rpc_server_listen(const char *listen_addr)
     229             : {
     230             :         struct spdk_rpc_server *server;
     231             :         int rc;
     232             : 
     233           3 :         server = calloc(1, sizeof(struct spdk_rpc_server));
     234           3 :         if (!server) {
     235           0 :                 SPDK_ERRLOG("Could not allocate new RPC server\n");
     236           0 :                 return NULL;
     237             :         }
     238             : 
     239           3 :         rc = _spdk_rpc_listen(listen_addr, server);
     240           3 :         if (rc) {
     241           0 :                 free(server);
     242           0 :                 return NULL;
     243             :         }
     244             : 
     245           3 :         return server;
     246           3 : }
     247             : 
     248             : void
     249           0 : spdk_rpc_accept(void)
     250             : {
     251           0 :         spdk_jsonrpc_server_poll(g_rpc_server.jsonrpc_server);
     252           0 : }
     253             : 
     254             : void
     255           0 : spdk_rpc_server_accept(struct spdk_rpc_server *server)
     256             : {
     257           0 :         assert(server != NULL);
     258           0 :         spdk_jsonrpc_server_poll(server->jsonrpc_server);
     259           0 : }
     260             : 
     261             : void
     262           2 : spdk_rpc_register_method(const char *method, spdk_rpc_method_handler func, uint32_t state_mask)
     263             : {
     264             :         struct spdk_rpc_method *m;
     265             : 
     266           2 :         m = _get_rpc_method_raw(method);
     267           2 :         if (m != NULL) {
     268           0 :                 SPDK_ERRLOG("duplicate RPC %s registered...\n", method);
     269           0 :                 g_rpcs_correct = false;
     270           0 :                 return;
     271             :         }
     272             : 
     273           2 :         m = calloc(1, sizeof(struct spdk_rpc_method));
     274           2 :         assert(m != NULL);
     275             : 
     276           2 :         m->name = strdup(method);
     277           2 :         assert(m->name != NULL);
     278             : 
     279           2 :         m->func = func;
     280           2 :         m->state_mask = state_mask;
     281             : 
     282             :         /* TODO: use a hash table or sorted list */
     283           2 :         SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
     284           2 : }
     285             : 
     286             : void
     287           0 : spdk_rpc_register_alias_deprecated(const char *method, const char *alias)
     288             : {
     289             :         struct spdk_rpc_method *m, *base;
     290             : 
     291           0 :         base = _get_rpc_method_raw(method);
     292           0 :         if (base == NULL) {
     293           0 :                 SPDK_ERRLOG("cannot create alias %s - method %s does not exist\n",
     294             :                             alias, method);
     295           0 :                 g_rpcs_correct = false;
     296           0 :                 return;
     297             :         }
     298             : 
     299           0 :         if (base->is_alias_of != NULL) {
     300           0 :                 SPDK_ERRLOG("cannot create alias %s of alias %s\n", alias, method);
     301           0 :                 g_rpcs_correct = false;
     302           0 :                 return;
     303             :         }
     304             : 
     305           0 :         m = calloc(1, sizeof(struct spdk_rpc_method));
     306           0 :         assert(m != NULL);
     307             : 
     308           0 :         m->name = strdup(alias);
     309           0 :         assert(m->name != NULL);
     310             : 
     311           0 :         m->is_alias_of = base;
     312           0 :         m->is_deprecated = true;
     313           0 :         m->state_mask = base->state_mask;
     314             : 
     315             :         /* TODO: use a hash table or sorted list */
     316           0 :         SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
     317           0 : }
     318             : 
     319             : bool
     320           0 : spdk_rpc_verify_methods(void)
     321             : {
     322           0 :         return g_rpcs_correct;
     323             : }
     324             : 
     325             : int
     326           3 : spdk_rpc_is_method_allowed(const char *method, uint32_t state_mask)
     327             : {
     328             :         struct spdk_rpc_method *m;
     329             : 
     330           3 :         if (!rpc_is_allowed(method)) {
     331           0 :                 return -ENOENT;
     332             :         }
     333             : 
     334           5 :         SLIST_FOREACH(m, &g_rpc_methods, slist) {
     335           4 :                 if (strcmp(m->name, method) != 0) {
     336           2 :                         continue;
     337             :                 }
     338             : 
     339           2 :                 if ((m->state_mask & state_mask) == state_mask) {
     340           1 :                         return 0;
     341             :                 } else {
     342           1 :                         return -EPERM;
     343             :                 }
     344             :         }
     345             : 
     346           1 :         return -ENOENT;
     347           3 : }
     348             : 
     349             : int
     350           2 : spdk_rpc_get_method_state_mask(const char *method, uint32_t *state_mask)
     351             : {
     352             :         struct spdk_rpc_method *m;
     353             : 
     354           4 :         SLIST_FOREACH(m, &g_rpc_methods, slist) {
     355           3 :                 if (strcmp(m->name, method) == 0) {
     356           1 :                         *state_mask = m->state_mask;
     357           1 :                         return 0;
     358             :                 }
     359           2 :         }
     360             : 
     361           1 :         return -ENOENT;
     362           2 : }
     363             : 
     364             : void
     365           0 : spdk_rpc_set_allowlist(const char **rpc_allowlist)
     366             : {
     367           0 :         spdk_strarray_free(g_rpcs_allowlist);
     368             : 
     369           0 :         if (rpc_allowlist == NULL) {
     370           0 :                 g_rpcs_allowlist = NULL;
     371           0 :                 return;
     372             :         }
     373             : 
     374           0 :         g_rpcs_allowlist = spdk_strarray_dup(rpc_allowlist);
     375           0 :         assert(g_rpcs_allowlist != NULL);
     376           0 : }
     377             : 
     378             : static void
     379           4 : _spdk_rpc_close(struct spdk_rpc_server *server)
     380             : {
     381           4 :         assert(server != NULL);
     382           4 :         assert(server->jsonrpc_server != NULL);
     383             : 
     384           4 :         if (server->listen_addr_unix.sun_path[0]) {
     385             :                 /* Delete the Unix socket file */
     386           4 :                 unlink(server->listen_addr_unix.sun_path);
     387           4 :                 server->listen_addr_unix.sun_path[0] = '\0';
     388           4 :         }
     389             : 
     390           4 :         spdk_jsonrpc_server_shutdown(server->jsonrpc_server);
     391           4 :         server->jsonrpc_server = NULL;
     392             : 
     393           4 :         if (server->lock_fd != -1) {
     394           4 :                 close(server->lock_fd);
     395           4 :                 server->lock_fd = -1;
     396           4 :         }
     397             : 
     398           4 :         if (server->lock_path[0]) {
     399           4 :                 unlink(server->lock_path);
     400           4 :                 server->lock_path[0] = '\0';
     401           4 :         }
     402           4 : }
     403             : 
     404           1 : SPDK_LOG_DEPRECATION_REGISTER(spdk_rpc_close, "spdk_rpc_close is deprecated", "v24.09", 0);
     405             : 
     406             : void
     407           1 : spdk_rpc_close(void)
     408             : {
     409           1 :         SPDK_LOG_DEPRECATED(spdk_rpc_close);
     410             : 
     411           1 :         if (g_rpc_server.jsonrpc_server) {
     412           1 :                 _spdk_rpc_close(&g_rpc_server);
     413           1 :         }
     414           1 : }
     415             : 
     416             : void
     417           3 : spdk_rpc_server_close(struct spdk_rpc_server *server)
     418             : {
     419           3 :         assert(server != NULL);
     420             : 
     421           3 :         _spdk_rpc_close(server);
     422             : 
     423           3 :         free(server);
     424           3 : }
     425             : 
     426             : struct rpc_get_methods {
     427             :         bool current;
     428             :         bool include_aliases;
     429             : };
     430             : 
     431             : static const struct spdk_json_object_decoder rpc_get_methods_decoders[] = {
     432             :         {"current", offsetof(struct rpc_get_methods, current), spdk_json_decode_bool, true},
     433             :         {"include_aliases", offsetof(struct rpc_get_methods, include_aliases), spdk_json_decode_bool, true},
     434             : };
     435             : 
     436             : static void
     437           2 : rpc_get_methods(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
     438             : {
     439           2 :         struct rpc_get_methods req = {};
     440             :         struct spdk_json_write_ctx *w;
     441             :         struct spdk_rpc_method *m;
     442             : 
     443           2 :         if (params != NULL) {
     444           2 :                 if (spdk_json_decode_object(params, rpc_get_methods_decoders,
     445             :                                             SPDK_COUNTOF(rpc_get_methods_decoders), &req)) {
     446           1 :                         SPDK_ERRLOG("spdk_json_decode_object failed\n");
     447           1 :                         spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
     448             :                                                          "Invalid parameters");
     449           1 :                         return;
     450             :                 }
     451           1 :         }
     452             : 
     453           1 :         w = spdk_jsonrpc_begin_result(request);
     454           1 :         spdk_json_write_array_begin(w);
     455           4 :         SLIST_FOREACH(m, &g_rpc_methods, slist) {
     456           3 :                 if (!rpc_is_allowed(m->name)) {
     457           0 :                         continue;
     458             :                 }
     459           3 :                 if (m->is_alias_of != NULL && !req.include_aliases) {
     460           0 :                         continue;
     461             :                 }
     462           3 :                 if (req.current && ((m->state_mask & g_rpc_state) != g_rpc_state)) {
     463           0 :                         continue;
     464             :                 }
     465           3 :                 spdk_json_write_string(w, m->name);
     466           3 :         }
     467           1 :         spdk_json_write_array_end(w);
     468           1 :         spdk_jsonrpc_end_result(request, w);
     469           2 : }
     470           1 : SPDK_RPC_REGISTER("rpc_get_methods", rpc_get_methods, SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
     471             : 
     472             : static void
     473           2 : rpc_spdk_get_version(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
     474             : {
     475             :         struct spdk_json_write_ctx *w;
     476             : 
     477           2 :         if (params != NULL) {
     478           1 :                 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
     479             :                                                  "spdk_get_version method requires no parameters");
     480           1 :                 return;
     481             :         }
     482             : 
     483           1 :         w = spdk_jsonrpc_begin_result(request);
     484           1 :         spdk_json_write_object_begin(w);
     485             : 
     486           1 :         spdk_json_write_named_string_fmt(w, "version", "%s", SPDK_VERSION_STRING);
     487           1 :         spdk_json_write_named_object_begin(w, "fields");
     488           1 :         spdk_json_write_named_uint32(w, "major", SPDK_VERSION_MAJOR);
     489           1 :         spdk_json_write_named_uint32(w, "minor", SPDK_VERSION_MINOR);
     490           1 :         spdk_json_write_named_uint32(w, "patch", SPDK_VERSION_PATCH);
     491           1 :         spdk_json_write_named_string_fmt(w, "suffix", "%s", SPDK_VERSION_SUFFIX);
     492             : #ifdef SPDK_GIT_COMMIT
     493           1 :         spdk_json_write_named_string_fmt(w, "commit", "%s", SPDK_GIT_COMMIT_STRING);
     494             : #endif
     495           1 :         spdk_json_write_object_end(w);
     496             : 
     497           1 :         spdk_json_write_object_end(w);
     498           1 :         spdk_jsonrpc_end_result(request, w);
     499           2 : }
     500           1 : SPDK_RPC_REGISTER("spdk_get_version", rpc_spdk_get_version,
     501             :                   SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)

Generated by: LCOV version 1.15