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 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 : 2893 : spdk_rpc_set_state(uint32_t state)
46 : : {
47 : 2893 : g_rpc_state = state;
48 : 2893 : }
49 : :
50 : : uint32_t
51 : 469588 : spdk_rpc_get_state(void)
52 : : {
53 : 469588 : return g_rpc_state;
54 : : }
55 : :
56 : : static bool
57 : 552361 : rpc_is_allowed(const char *name)
58 : : {
59 : : size_t i;
60 : :
61 [ + + ]: 552361 : if (g_rpcs_allowlist == NULL) {
62 : 541471 : return true;
63 : : }
64 : :
65 [ + + ]: 32460 : for (i = 0; g_rpcs_allowlist[i] != NULL; i++) {
66 [ + + - + : 21717 : if (strcmp(name, g_rpcs_allowlist[i]) == 0) {
+ + ]
67 : 147 : return true;
68 : : }
69 : : }
70 : :
71 : 10743 : return false;
72 : : }
73 : :
74 : :
75 : : static struct spdk_rpc_method *
76 : 583789 : _get_rpc_method(const struct spdk_json_val *method)
77 : : {
78 : : struct spdk_rpc_method *m;
79 : :
80 [ + + ]: 57642080 : SLIST_FOREACH(m, &g_rpc_methods, slist) {
81 [ + + ]: 57129039 : if (spdk_json_strequal(method, m->name)) {
82 [ + + ]: 70751 : if (!rpc_is_allowed(m->name)) {
83 : 21 : return NULL;
84 : : }
85 : 70730 : return m;
86 : : }
87 : : }
88 : :
89 : 513038 : return NULL;
90 : : }
91 : :
92 : : static struct spdk_rpc_method *
93 : 529861 : _get_rpc_method_raw(const char *method)
94 : : {
95 : 253497 : struct spdk_json_val method_val;
96 : :
97 : 529861 : method_val.type = SPDK_JSON_VAL_STRING;
98 [ - + ]: 529861 : method_val.len = strlen(method);
99 : 529861 : method_val.start = (char *)method;
100 : :
101 : 529861 : return _get_rpc_method(&method_val);
102 : : }
103 : :
104 : : static void
105 : 53928 : 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 [ - + ]: 53928 : assert(method != NULL);
112 : :
113 : 53928 : m = _get_rpc_method(method);
114 [ + + ]: 53928 : if (m == NULL) {
115 : 27 : spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND, "Method not found");
116 : 27 : return;
117 : : }
118 : :
119 [ + + ]: 53901 : if (m->is_alias_of != NULL) {
120 [ + + + + : 18 : if (m->is_deprecated && !m->deprecation_warning_printed) {
- + + + ]
121 : 6 : SPDK_WARNLOG("RPC method %s is deprecated. Use %s instead.\n", m->name, m->is_alias_of->name);
122 : 6 : m->deprecation_warning_printed = true;
123 : : }
124 : 18 : m = m->is_alias_of;
125 : : }
126 : :
127 [ + + ]: 53901 : if ((m->state_mask & g_rpc_state) == g_rpc_state) {
128 : 53889 : m->func(request, params);
129 : : } else {
130 [ + + ]: 12 : if (g_rpc_state == SPDK_RPC_STARTUP) {
131 : 6 : 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 : : } else {
137 : 6 : 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 : : }
147 : :
148 : : static int
149 : 3803 : _spdk_rpc_listen(const char *listen_addr, struct spdk_rpc_server *server)
150 : : {
151 : : int rc;
152 : :
153 [ - + ]: 3803 : assert(listen_addr != NULL);
154 : :
155 : 3803 : server->listen_addr_unix.sun_family = AF_UNIX;
156 : 3803 : rc = snprintf(server->listen_addr_unix.sun_path,
157 : : sizeof(server->listen_addr_unix.sun_path),
158 : : "%s", listen_addr);
159 [ + - - + ]: 3803 : 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 : 3803 : rc = snprintf(server->lock_path, sizeof(server->lock_path), "%s.lock",
165 : 3803 : server->listen_addr_unix.sun_path);
166 [ + - - + ]: 3803 : 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 [ - + ]: 3803 : server->lock_fd = open(server->lock_path, O_RDWR | O_CREAT, 0600);
172 [ - + ]: 3803 : 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 : 3803 : rc = flock(server->lock_fd, LOCK_EX | LOCK_NB);
179 [ + + ]: 3803 : if (rc != 0) {
180 : 21 : SPDK_ERRLOG("RPC Unix domain socket path %s in use. Specify another.\n",
181 : : server->listen_addr_unix.sun_path);
182 : 21 : 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 [ - + ]: 3782 : unlink(server->listen_addr_unix.sun_path);
190 : :
191 : 4481 : server->jsonrpc_server = spdk_jsonrpc_server_listen(AF_UNIX, 0,
192 : 3782 : (struct sockaddr *) & server->listen_addr_unix,
193 : : sizeof(server->listen_addr_unix),
194 : : jsonrpc_handler);
195 [ - + ]: 3782 : 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 : 3782 : return 0;
203 : : }
204 : :
205 [ - + ]: 3547 : SPDK_LOG_DEPRECATION_REGISTER(spdk_rpc_listen, "spdk_rpc_listen is deprecated", "v24.09", 0);
206 : :
207 : : int
208 : 27 : spdk_rpc_listen(const char *listen_addr)
209 : : {
210 : : struct spdk_rpc_server *server;
211 : : int rc;
212 : :
213 : 27 : SPDK_LOG_DEPRECATED(spdk_rpc_listen);
214 : :
215 [ - + ]: 27 : memset(&g_rpc_server.listen_addr_unix, 0, sizeof(g_rpc_server.listen_addr_unix));
216 : 27 : server = &g_rpc_server;
217 : :
218 : 27 : rc = _spdk_rpc_listen(listen_addr, server);
219 [ - + ]: 27 : if (rc) {
220 : 0 : server->listen_addr_unix.sun_path[0] = '\0';
221 : 0 : server->lock_path[0] = '\0';
222 : : }
223 : :
224 : 27 : return rc;
225 : : }
226 : :
227 : : struct spdk_rpc_server *
228 : 3776 : spdk_rpc_server_listen(const char *listen_addr)
229 : : {
230 : : struct spdk_rpc_server *server;
231 : : int rc;
232 : :
233 : 3776 : server = calloc(1, sizeof(struct spdk_rpc_server));
234 [ - + ]: 3776 : if (!server) {
235 : 0 : SPDK_ERRLOG("Could not allocate new RPC server\n");
236 : 0 : return NULL;
237 : : }
238 : :
239 : 3776 : rc = _spdk_rpc_listen(listen_addr, server);
240 [ + + ]: 3776 : if (rc) {
241 : 21 : free(server);
242 : 21 : return NULL;
243 : : }
244 : :
245 : 3755 : return server;
246 : : }
247 : :
248 : : void
249 : 260 : spdk_rpc_accept(void)
250 : : {
251 : 260 : spdk_jsonrpc_server_poll(g_rpc_server.jsonrpc_server);
252 : 260 : }
253 : :
254 : : void
255 : 4742574 : spdk_rpc_server_accept(struct spdk_rpc_server *server)
256 : : {
257 [ - + ]: 4742574 : assert(server != NULL);
258 : 4742574 : spdk_jsonrpc_server_poll(server->jsonrpc_server);
259 : 4742574 : }
260 : :
261 : : void
262 : 513032 : 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 : 513032 : m = _get_rpc_method_raw(method);
267 [ - + ]: 513032 : 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 : 513032 : m = calloc(1, sizeof(struct spdk_rpc_method));
274 [ - + ]: 513032 : assert(m != NULL);
275 : :
276 [ - + ]: 513032 : m->name = strdup(method);
277 [ - + ]: 513032 : assert(m->name != NULL);
278 : :
279 : 513032 : m->func = func;
280 : 513032 : m->state_mask = state_mask;
281 : :
282 : : /* TODO: use a hash table or sorted list */
283 : 513032 : SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
284 : : }
285 : :
286 : : void
287 : 16829 : spdk_rpc_register_alias_deprecated(const char *method, const char *alias)
288 : : {
289 : : struct spdk_rpc_method *m, *base;
290 : :
291 : 16829 : base = _get_rpc_method_raw(method);
292 [ - + ]: 16829 : 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 [ - + ]: 16829 : 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 : 16829 : m = calloc(1, sizeof(struct spdk_rpc_method));
306 [ - + ]: 16829 : assert(m != NULL);
307 : :
308 [ - + ]: 16829 : m->name = strdup(alias);
309 [ - + ]: 16829 : assert(m->name != NULL);
310 : :
311 : 16829 : m->is_alias_of = base;
312 : 16829 : m->is_deprecated = true;
313 : 16829 : m->state_mask = base->state_mask;
314 : :
315 : : /* TODO: use a hash table or sorted list */
316 : 16829 : SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
317 : : }
318 : :
319 : : bool
320 : 3758 : spdk_rpc_verify_methods(void)
321 : : {
322 [ - + ]: 3758 : return g_rpcs_correct;
323 : : }
324 : :
325 : : int
326 : 18 : spdk_rpc_is_method_allowed(const char *method, uint32_t state_mask)
327 : : {
328 : : struct spdk_rpc_method *m;
329 : :
330 [ - + ]: 18 : if (!rpc_is_allowed(method)) {
331 : 0 : return -ENOENT;
332 : : }
333 : :
334 [ + + ]: 30 : SLIST_FOREACH(m, &g_rpc_methods, slist) {
335 [ + + - + : 24 : if (strcmp(m->name, method) != 0) {
+ + ]
336 : 12 : continue;
337 : : }
338 : :
339 [ + + ]: 12 : if ((m->state_mask & state_mask) == state_mask) {
340 : 6 : return 0;
341 : : } else {
342 : 6 : return -EPERM;
343 : : }
344 : : }
345 : :
346 : 6 : return -ENOENT;
347 : : }
348 : :
349 : : int
350 : 9790 : spdk_rpc_get_method_state_mask(const char *method, uint32_t *state_mask)
351 : : {
352 : : struct spdk_rpc_method *m;
353 : :
354 [ + + ]: 1043726 : SLIST_FOREACH(m, &g_rpc_methods, slist) {
355 [ + + - + : 1043720 : if (strcmp(m->name, method) == 0) {
+ + ]
356 : 9784 : *state_mask = m->state_mask;
357 : 9784 : return 0;
358 : : }
359 : : }
360 : :
361 : 6 : return -ENOENT;
362 : : }
363 : :
364 : : void
365 : 5759 : spdk_rpc_set_allowlist(const char **rpc_allowlist)
366 : : {
367 : 5759 : spdk_strarray_free(g_rpcs_allowlist);
368 : :
369 [ + + ]: 5759 : if (rpc_allowlist == NULL) {
370 : 5717 : g_rpcs_allowlist = NULL;
371 : 5717 : return;
372 : : }
373 : :
374 : 42 : g_rpcs_allowlist = spdk_strarray_dup(rpc_allowlist);
375 [ - + ]: 42 : assert(g_rpcs_allowlist != NULL);
376 : : }
377 : :
378 : : static void
379 : 3782 : _spdk_rpc_close(struct spdk_rpc_server *server)
380 : : {
381 [ - + ]: 3782 : assert(server != NULL);
382 [ - + ]: 3782 : assert(server->jsonrpc_server != NULL);
383 : :
384 [ + - ]: 3782 : if (server->listen_addr_unix.sun_path[0]) {
385 : : /* Delete the Unix socket file */
386 [ - + ]: 3782 : unlink(server->listen_addr_unix.sun_path);
387 : 3782 : server->listen_addr_unix.sun_path[0] = '\0';
388 : : }
389 : :
390 : 3782 : spdk_jsonrpc_server_shutdown(server->jsonrpc_server);
391 : 3782 : server->jsonrpc_server = NULL;
392 : :
393 [ + - ]: 3782 : if (server->lock_fd != -1) {
394 : 3782 : close(server->lock_fd);
395 : 3782 : server->lock_fd = -1;
396 : : }
397 : :
398 [ + - ]: 3782 : if (server->lock_path[0]) {
399 [ - + ]: 3782 : unlink(server->lock_path);
400 : 3782 : server->lock_path[0] = '\0';
401 : : }
402 : 3782 : }
403 : :
404 [ - + ]: 3547 : SPDK_LOG_DEPRECATION_REGISTER(spdk_rpc_close, "spdk_rpc_close is deprecated", "v24.09", 0);
405 : :
406 : : void
407 : 33 : spdk_rpc_close(void)
408 : : {
409 : 33 : SPDK_LOG_DEPRECATED(spdk_rpc_close);
410 : :
411 [ + + ]: 33 : if (g_rpc_server.jsonrpc_server) {
412 : 27 : _spdk_rpc_close(&g_rpc_server);
413 : : }
414 : 33 : }
415 : :
416 : : void
417 : 3755 : spdk_rpc_server_close(struct spdk_rpc_server *server)
418 : : {
419 [ - + ]: 3755 : assert(server != NULL);
420 : :
421 : 3755 : _spdk_rpc_close(server);
422 : :
423 : 3755 : free(server);
424 : 3755 : }
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 : 2285 : rpc_get_methods(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
438 : : {
439 : 2285 : struct rpc_get_methods req = {};
440 : : struct spdk_json_write_ctx *w;
441 : : struct spdk_rpc_method *m;
442 : :
443 [ + + ]: 2285 : if (params != NULL) {
444 [ + + ]: 562 : if (spdk_json_decode_object(params, rpc_get_methods_decoders,
445 : : SPDK_COUNTOF(rpc_get_methods_decoders), &req)) {
446 : 6 : SPDK_ERRLOG("spdk_json_decode_object failed\n");
447 : 6 : spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
448 : : "Invalid parameters");
449 : 6 : return;
450 : : }
451 : : }
452 : :
453 : 2279 : w = spdk_jsonrpc_begin_result(request);
454 : 2279 : spdk_json_write_array_begin(w);
455 [ + + ]: 483871 : SLIST_FOREACH(m, &g_rpc_methods, slist) {
456 [ + + ]: 481592 : if (!rpc_is_allowed(m->name)) {
457 : 10722 : continue;
458 : : }
459 [ + + + + : 470870 : if (m->is_alias_of != NULL && !req.include_aliases) {
+ + ]
460 : 12807 : continue;
461 : : }
462 [ + + + + : 458063 : if (req.current && ((m->state_mask & g_rpc_state) != g_rpc_state)) {
+ + ]
463 : 10894 : continue;
464 : : }
465 : 447169 : spdk_json_write_string(w, m->name);
466 : : }
467 : 2279 : spdk_json_write_array_end(w);
468 : 2279 : spdk_jsonrpc_end_result(request, w);
469 : : }
470 : 3547 : SPDK_RPC_REGISTER("rpc_get_methods", rpc_get_methods, SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
471 : :
472 : : static void
473 : 33 : 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 [ + + ]: 33 : if (params != NULL) {
478 : 6 : spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
479 : : "spdk_get_version method requires no parameters");
480 : 6 : return;
481 : : }
482 : :
483 : 27 : w = spdk_jsonrpc_begin_result(request);
484 : 27 : spdk_json_write_object_begin(w);
485 : :
486 : 27 : spdk_json_write_named_string_fmt(w, "version", "%s", SPDK_VERSION_STRING);
487 : 27 : spdk_json_write_named_object_begin(w, "fields");
488 : 27 : spdk_json_write_named_uint32(w, "major", SPDK_VERSION_MAJOR);
489 : 27 : spdk_json_write_named_uint32(w, "minor", SPDK_VERSION_MINOR);
490 : 27 : spdk_json_write_named_uint32(w, "patch", SPDK_VERSION_PATCH);
491 : 27 : spdk_json_write_named_string_fmt(w, "suffix", "%s", SPDK_VERSION_SUFFIX);
492 : : #ifdef SPDK_GIT_COMMIT
493 : 27 : spdk_json_write_named_string_fmt(w, "commit", "%s", SPDK_GIT_COMMIT_STRING);
494 : : #endif
495 : 27 : spdk_json_write_object_end(w);
496 : :
497 : 27 : spdk_json_write_object_end(w);
498 : 27 : spdk_jsonrpc_end_result(request, w);
499 : : }
500 : 3547 : SPDK_RPC_REGISTER("spdk_get_version", rpc_spdk_get_version,
501 : : SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
|