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)
|