LCOV - code coverage report
Current view: top level - lib/rpc - rpc.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 119 187 63.6 %
Date: 2024-07-15 16:19:09 Functions: 13 19 68.4 %

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

Generated by: LCOV version 1.15