LCOV - code coverage report
Current view: top level - spdk/lib/rpc - rpc.c (source / functions) Hit Total Coverage
Test: Combined Lines: 160 187 85.6 %
Date: 2024-07-13 02:54:37 Functions: 19 19 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 99 128 77.3 %

           Branch data     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                 :       4529 : spdk_rpc_set_state(uint32_t state)
      42                 :            : {
      43                 :       4529 :         g_rpc_state = state;
      44                 :       4529 : }
      45                 :            : 
      46                 :            : uint32_t
      47                 :     537134 : spdk_rpc_get_state(void)
      48                 :            : {
      49                 :     537134 :         return g_rpc_state;
      50                 :            : }
      51                 :            : 
      52                 :            : static bool
      53                 :     455047 : rpc_is_allowed(const char *name)
      54                 :            : {
      55                 :            :         size_t i;
      56                 :            : 
      57         [ +  + ]:     455047 :         if (g_rpcs_allowlist == NULL) {
      58                 :     444873 :                 return true;
      59                 :            :         }
      60                 :            : 
      61         [ +  + ]:      30312 :         for (i = 0; g_rpcs_allowlist[i] != NULL; i++) {
      62   [ +  +  -  +  :      20285 :                 if (strcmp(name, g_rpcs_allowlist[i]) == 0) {
                   +  + ]
      63                 :        147 :                         return true;
      64                 :            :                 }
      65                 :            :         }
      66                 :            : 
      67                 :      10027 :         return false;
      68                 :            : }
      69                 :            : 
      70                 :            : 
      71                 :            : static struct spdk_rpc_method *
      72                 :     491715 : _get_rpc_method(const struct spdk_json_val *method)
      73                 :            : {
      74                 :            :         struct spdk_rpc_method *m;
      75                 :            : 
      76         [ +  + ]:   43406351 :         SLIST_FOREACH(m, &g_rpc_methods, slist) {
      77         [ +  + ]:   42971228 :                 if (spdk_json_strequal(method, m->name)) {
      78         [ +  + ]:      56590 :                         if (!rpc_is_allowed(m->name)) {
      79                 :         21 :                                 return NULL;
      80                 :            :                         }
      81                 :      56569 :                         return m;
      82                 :            :                 }
      83                 :            :         }
      84                 :            : 
      85                 :     435125 :         return NULL;
      86                 :            : }
      87                 :            : 
      88                 :            : static struct spdk_rpc_method *
      89                 :     451550 : _get_rpc_method_raw(const char *method)
      90                 :            : {
      91                 :     214679 :         struct spdk_json_val method_val;
      92                 :            : 
      93                 :     451550 :         method_val.type = SPDK_JSON_VAL_STRING;
      94         [ -  + ]:     451550 :         method_val.len = strlen(method);
      95                 :     451550 :         method_val.start = (char *)method;
      96                 :            : 
      97                 :     451550 :         return _get_rpc_method(&method_val);
      98                 :            : }
      99                 :            : 
     100                 :            : static void
     101                 :      40165 : 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         [ -  + ]:      40165 :         assert(method != NULL);
     108                 :            : 
     109                 :      40165 :         m = _get_rpc_method(method);
     110         [ +  + ]:      40165 :         if (m == NULL) {
     111                 :         27 :                 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND, "Method not found");
     112                 :         27 :                 return;
     113                 :            :         }
     114                 :            : 
     115         [ +  + ]:      40138 :         if (m->is_alias_of != NULL) {
     116   [ +  +  +  +  :         18 :                 if (m->is_deprecated && !m->deprecation_warning_printed) {
             -  +  +  + ]
     117                 :          6 :                         SPDK_WARNLOG("RPC method %s is deprecated.  Use %s instead.\n", m->name, m->is_alias_of->name);
     118                 :          6 :                         m->deprecation_warning_printed = true;
     119                 :            :                 }
     120                 :         18 :                 m = m->is_alias_of;
     121                 :            :         }
     122                 :            : 
     123         [ +  + ]:      40138 :         if ((m->state_mask & g_rpc_state) == g_rpc_state) {
     124                 :      40126 :                 m->func(request, params);
     125                 :            :         } else {
     126         [ +  + ]:         12 :                 if (g_rpc_state == SPDK_RPC_STARTUP) {
     127                 :          6 :                         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                 :          6 :                         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                 :       4418 : spdk_rpc_listen(const char *listen_addr)
     146                 :            : {
     147                 :            :         int rc;
     148                 :            : 
     149                 :       4418 :         memset(&g_rpc_listen_addr_unix, 0, sizeof(g_rpc_listen_addr_unix));
     150                 :            : 
     151                 :       4418 :         g_rpc_listen_addr_unix.sun_family = AF_UNIX;
     152                 :       4418 :         rc = snprintf(g_rpc_listen_addr_unix.sun_path,
     153                 :            :                       sizeof(g_rpc_listen_addr_unix.sun_path),
     154                 :            :                       "%s", listen_addr);
     155   [ +  -  -  + ]:       4418 :         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                 :       4418 :         rc = snprintf(g_rpc_lock_path, sizeof(g_rpc_lock_path), "%s.lock",
     162                 :            :                       g_rpc_listen_addr_unix.sun_path);
     163   [ +  -  -  + ]:       4418 :         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                 :       4418 :         g_rpc_lock_fd = open(g_rpc_lock_path, O_RDWR | O_CREAT, 0600);
     171         [ -  + ]:       4418 :         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                 :       4418 :         rc = flock(g_rpc_lock_fd, LOCK_EX | LOCK_NB);
     180         [ +  + ]:       4418 :         if (rc != 0) {
     181                 :         47 :                 SPDK_ERRLOG("RPC Unix domain socket path %s in use. Specify another.\n",
     182                 :            :                             g_rpc_listen_addr_unix.sun_path);
     183                 :         47 :                 g_rpc_listen_addr_unix.sun_path[0] = '\0';
     184                 :         47 :                 g_rpc_lock_path[0] = '\0';
     185                 :         47 :                 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                 :       4371 :         unlink(g_rpc_listen_addr_unix.sun_path);
     193                 :            : 
     194                 :       4371 :         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         [ -  + ]:       4371 :         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                 :       4371 :         return 0;
     208                 :            : }
     209                 :            : 
     210                 :            : void
     211                 :    3918132 : spdk_rpc_accept(void)
     212                 :            : {
     213                 :    3918132 :         spdk_jsonrpc_server_poll(g_jsonrpc_server);
     214                 :    3918132 : }
     215                 :            : 
     216                 :            : void
     217                 :     435119 : 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                 :     435119 :         m = _get_rpc_method_raw(method);
     222         [ -  + ]:     435119 :         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                 :     435119 :         m = calloc(1, sizeof(struct spdk_rpc_method));
     229         [ -  + ]:     435119 :         assert(m != NULL);
     230                 :            : 
     231         [ -  + ]:     435119 :         m->name = strdup(method);
     232         [ -  + ]:     435119 :         assert(m->name != NULL);
     233                 :            : 
     234                 :     435119 :         m->func = func;
     235                 :     435119 :         m->state_mask = state_mask;
     236                 :            : 
     237                 :            :         /* TODO: use a hash table or sorted list */
     238                 :     435119 :         SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
     239                 :            : }
     240                 :            : 
     241                 :            : void
     242                 :      16431 : spdk_rpc_register_alias_deprecated(const char *method, const char *alias)
     243                 :            : {
     244                 :            :         struct spdk_rpc_method *m, *base;
     245                 :            : 
     246                 :      16431 :         base = _get_rpc_method_raw(method);
     247         [ -  + ]:      16431 :         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         [ -  + ]:      16431 :         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                 :      16431 :         m = calloc(1, sizeof(struct spdk_rpc_method));
     261         [ -  + ]:      16431 :         assert(m != NULL);
     262                 :            : 
     263         [ -  + ]:      16431 :         m->name = strdup(alias);
     264         [ -  + ]:      16431 :         assert(m->name != NULL);
     265                 :            : 
     266                 :      16431 :         m->is_alias_of = base;
     267                 :      16431 :         m->is_deprecated = true;
     268                 :      16431 :         m->state_mask = base->state_mask;
     269                 :            : 
     270                 :            :         /* TODO: use a hash table or sorted list */
     271                 :      16431 :         SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
     272                 :            : }
     273                 :            : 
     274                 :            : bool
     275                 :       4391 : spdk_rpc_verify_methods(void)
     276                 :            : {
     277         [ -  + ]:       4391 :         return g_rpcs_correct;
     278                 :            : }
     279                 :            : 
     280                 :            : int
     281                 :         18 : spdk_rpc_is_method_allowed(const char *method, uint32_t state_mask)
     282                 :            : {
     283                 :            :         struct spdk_rpc_method *m;
     284                 :            : 
     285         [ -  + ]:         18 :         if (!rpc_is_allowed(method)) {
     286                 :          0 :                 return -ENOENT;
     287                 :            :         }
     288                 :            : 
     289         [ +  + ]:         30 :         SLIST_FOREACH(m, &g_rpc_methods, slist) {
     290   [ +  +  -  +  :         24 :                 if (strcmp(m->name, method) != 0) {
                   +  + ]
     291                 :         12 :                         continue;
     292                 :            :                 }
     293                 :            : 
     294         [ +  + ]:         12 :                 if ((m->state_mask & state_mask) == state_mask) {
     295                 :          6 :                         return 0;
     296                 :            :                 } else {
     297                 :          6 :                         return -EPERM;
     298                 :            :                 }
     299                 :            :         }
     300                 :            : 
     301                 :          6 :         return -ENOENT;
     302                 :            : }
     303                 :            : 
     304                 :            : int
     305                 :       8604 : spdk_rpc_get_method_state_mask(const char *method, uint32_t *state_mask)
     306                 :            : {
     307                 :            :         struct spdk_rpc_method *m;
     308                 :            : 
     309         [ +  + ]:     835274 :         SLIST_FOREACH(m, &g_rpc_methods, slist) {
     310   [ +  +  -  +  :     835268 :                 if (strcmp(m->name, method) == 0) {
                   +  + ]
     311                 :       8598 :                         *state_mask = m->state_mask;
     312                 :       8598 :                         return 0;
     313                 :            :                 }
     314                 :            :         }
     315                 :            : 
     316                 :          6 :         return -ENOENT;
     317                 :            : }
     318                 :            : 
     319                 :            : void
     320                 :       2968 : spdk_rpc_set_allowlist(const char **rpc_allowlist)
     321                 :            : {
     322                 :       2968 :         spdk_strarray_free(g_rpcs_allowlist);
     323                 :            : 
     324         [ +  + ]:       2968 :         if (rpc_allowlist == NULL) {
     325                 :       2947 :                 g_rpcs_allowlist = NULL;
     326                 :       2947 :                 return;
     327                 :            :         }
     328                 :            : 
     329                 :         21 :         g_rpcs_allowlist = spdk_strarray_dup(rpc_allowlist);
     330         [ -  + ]:         21 :         assert(g_rpcs_allowlist != NULL);
     331                 :            : }
     332                 :            : 
     333                 :            : void
     334                 :       4669 : spdk_rpc_close(void)
     335                 :            : {
     336         [ +  + ]:       4669 :         if (g_jsonrpc_server) {
     337         [ +  - ]:       4371 :                 if (g_rpc_listen_addr_unix.sun_path[0]) {
     338                 :            :                         /* Delete the Unix socket file */
     339         [ -  + ]:       4371 :                         unlink(g_rpc_listen_addr_unix.sun_path);
     340                 :       4371 :                         g_rpc_listen_addr_unix.sun_path[0] = '\0';
     341                 :            :                 }
     342                 :            : 
     343                 :       4371 :                 spdk_jsonrpc_server_shutdown(g_jsonrpc_server);
     344                 :       4371 :                 g_jsonrpc_server = NULL;
     345                 :            : 
     346         [ +  - ]:       4371 :                 if (g_rpc_lock_fd != -1) {
     347                 :       4371 :                         close(g_rpc_lock_fd);
     348                 :       4371 :                         g_rpc_lock_fd = -1;
     349                 :            :                 }
     350                 :            : 
     351         [ +  - ]:       4371 :                 if (g_rpc_lock_path[0]) {
     352         [ -  + ]:       4371 :                         unlink(g_rpc_lock_path);
     353                 :       4371 :                         g_rpc_lock_path[0] = '\0';
     354                 :            :                 }
     355                 :            :         }
     356                 :       4669 : }
     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                 :       2012 : rpc_get_methods(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
     370                 :            : {
     371                 :       2012 :         struct rpc_get_methods req = {};
     372                 :            :         struct spdk_json_write_ctx *w;
     373                 :            :         struct spdk_rpc_method *m;
     374                 :            : 
     375         [ +  + ]:       2012 :         if (params != NULL) {
     376         [ +  + ]:        562 :                 if (spdk_json_decode_object(params, rpc_get_methods_decoders,
     377                 :            :                                             SPDK_COUNTOF(rpc_get_methods_decoders), &req)) {
     378                 :          6 :                         SPDK_ERRLOG("spdk_json_decode_object failed\n");
     379                 :          6 :                         spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
     380                 :            :                                                          "Invalid parameters");
     381                 :          6 :                         return;
     382                 :            :                 }
     383                 :            :         }
     384                 :            : 
     385                 :       2006 :         w = spdk_jsonrpc_begin_result(request);
     386                 :       2006 :         spdk_json_write_array_begin(w);
     387         [ +  + ]:     400445 :         SLIST_FOREACH(m, &g_rpc_methods, slist) {
     388         [ +  + ]:     398439 :                 if (!rpc_is_allowed(m->name)) {
     389                 :      10006 :                         continue;
     390                 :            :                 }
     391   [ +  +  +  +  :     388433 :                 if (m->is_alias_of != NULL && !req.include_aliases) {
                   +  + ]
     392                 :      11091 :                         continue;
     393                 :            :                 }
     394   [ +  +  +  +  :     377342 :                 if (req.current && ((m->state_mask & g_rpc_state) != g_rpc_state)) {
                   +  + ]
     395                 :      10165 :                         continue;
     396                 :            :                 }
     397                 :     367177 :                 spdk_json_write_string(w, m->name);
     398                 :            :         }
     399                 :       2006 :         spdk_json_write_array_end(w);
     400                 :       2006 :         spdk_jsonrpc_end_result(request, w);
     401                 :            : }
     402                 :       3620 : SPDK_RPC_REGISTER("rpc_get_methods", rpc_get_methods, SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
     403                 :            : 
     404                 :            : static void
     405                 :         33 : 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         [ +  + ]:         33 :         if (params != NULL) {
     410                 :          6 :                 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
     411                 :            :                                                  "spdk_get_version method requires no parameters");
     412                 :          6 :                 return;
     413                 :            :         }
     414                 :            : 
     415                 :         27 :         w = spdk_jsonrpc_begin_result(request);
     416                 :         27 :         spdk_json_write_object_begin(w);
     417                 :            : 
     418                 :         27 :         spdk_json_write_named_string_fmt(w, "version", "%s", SPDK_VERSION_STRING);
     419                 :         27 :         spdk_json_write_named_object_begin(w, "fields");
     420                 :         27 :         spdk_json_write_named_uint32(w, "major", SPDK_VERSION_MAJOR);
     421                 :         27 :         spdk_json_write_named_uint32(w, "minor", SPDK_VERSION_MINOR);
     422                 :         27 :         spdk_json_write_named_uint32(w, "patch", SPDK_VERSION_PATCH);
     423                 :         27 :         spdk_json_write_named_string_fmt(w, "suffix", "%s", SPDK_VERSION_SUFFIX);
     424                 :            : #ifdef SPDK_GIT_COMMIT
     425                 :         27 :         spdk_json_write_named_string_fmt(w, "commit", "%s", SPDK_GIT_COMMIT_STRING);
     426                 :            : #endif
     427                 :         27 :         spdk_json_write_object_end(w);
     428                 :            : 
     429                 :         27 :         spdk_json_write_object_end(w);
     430                 :         27 :         spdk_jsonrpc_end_result(request, w);
     431                 :            : }
     432                 :       3620 : SPDK_RPC_REGISTER("spdk_get_version", rpc_spdk_get_version,
     433                 :            :                   SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)

Generated by: LCOV version 1.14