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 : 4460 : spdk_rpc_set_state(uint32_t state)
42 : : {
43 : 4460 : g_rpc_state = state;
44 : 4460 : }
45 : :
46 : : uint32_t
47 : 498291 : spdk_rpc_get_state(void)
48 : : {
49 : 498291 : return g_rpc_state;
50 : : }
51 : :
52 : : static bool
53 : 446546 : rpc_is_allowed(const char *name)
54 : : {
55 : 9 : size_t i;
56 : :
57 [ + + ]: 446546 : if (g_rpcs_allowlist == NULL) {
58 : 436340 : return true;
59 : : }
60 : :
61 [ + + + - : 30408 : for (i = 0; g_rpcs_allowlist[i] != NULL; i++) {
+ + ]
62 [ + + - + : 20349 : if (strcmp(name, g_rpcs_allowlist[i]) == 0) {
+ + - + +
+ ]
63 : 147 : return true;
64 : : }
65 : 978 : }
66 : :
67 : 10059 : return false;
68 : 7531 : }
69 : :
70 : :
71 : : static struct spdk_rpc_method *
72 : 476097 : _get_rpc_method(const struct spdk_json_val *method)
73 : : {
74 : 6 : struct spdk_rpc_method *m;
75 : :
76 [ + + + - : 42252533 : SLIST_FOREACH(m, &g_rpc_methods, slist) {
+ - + - ]
77 [ + + + - : 41830990 : if (spdk_json_strequal(method, m->name)) {
+ + ]
78 [ + + - + : 54554 : if (!rpc_is_allowed(m->name)) {
+ + ]
79 : 21 : return NULL;
80 : : }
81 : 54533 : return m;
82 : : }
83 : 1338462 : }
84 : :
85 : 421543 : return NULL;
86 : 14795 : }
87 : :
88 : : static struct spdk_rpc_method *
89 : 437139 : _get_rpc_method_raw(const char *method)
90 : : {
91 : 185888 : struct spdk_json_val method_val;
92 : :
93 [ + - ]: 437139 : method_val.type = SPDK_JSON_VAL_STRING;
94 [ + + + - ]: 437139 : method_val.len = strlen(method);
95 : 437139 : method_val.start = (char *)method;
96 : :
97 : 437141 : return _get_rpc_method(&method_val);
98 : 2 : }
99 : :
100 : : static void
101 : 38958 : jsonrpc_handler(struct spdk_jsonrpc_request *request,
102 : : const struct spdk_json_val *method,
103 : : const struct spdk_json_val *params)
104 : : {
105 : 4 : struct spdk_rpc_method *m;
106 : :
107 [ + + # # ]: 38958 : assert(method != NULL);
108 : :
109 : 38958 : m = _get_rpc_method(method);
110 [ + + ]: 38958 : 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 [ + + + - : 38931 : 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 : 1 : }
120 [ + - + - ]: 18 : m = m->is_alias_of;
121 : 3 : }
122 : :
123 [ + + + - : 38931 : if ((m->state_mask & g_rpc_state) == g_rpc_state) {
+ + ]
124 [ + - + - : 38919 : m->func(request, params);
- + + - ]
125 : 409 : } 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 : 1 : } 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 [ - + ]: 413 : }
143 : :
144 : : int
145 : 4356 : spdk_rpc_listen(const char *listen_addr)
146 : : {
147 : 1 : int rc;
148 : :
149 [ + - ]: 4356 : memset(&g_rpc_listen_addr_unix, 0, sizeof(g_rpc_listen_addr_unix));
150 : :
151 : 4356 : g_rpc_listen_addr_unix.sun_family = AF_UNIX;
152 : 4356 : rc = snprintf(g_rpc_listen_addr_unix.sun_path,
153 : : sizeof(g_rpc_listen_addr_unix.sun_path),
154 : 184 : "%s", listen_addr);
155 [ + - - + ]: 4356 : 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 : 4356 : rc = snprintf(g_rpc_lock_path, sizeof(g_rpc_lock_path), "%s.lock",
162 : : g_rpc_listen_addr_unix.sun_path);
163 [ + - - + ]: 4356 : 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 [ + - ]: 4356 : g_rpc_lock_fd = open(g_rpc_lock_path, O_RDWR | O_CREAT, 0600);
171 [ - + ]: 4356 : 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 : 4356 : rc = flock(g_rpc_lock_fd, LOCK_EX | LOCK_NB);
180 [ + + ]: 4356 : 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 [ + - ]: 4309 : unlink(g_rpc_listen_addr_unix.sun_path);
193 : :
194 : 4309 : 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 [ + + ]: 4309 : 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 : 4309 : return 0;
208 : 184 : }
209 : :
210 : : void
211 : 3821365 : spdk_rpc_accept(void)
212 : : {
213 : 3821365 : spdk_jsonrpc_server_poll(g_jsonrpc_server);
214 : 3821365 : }
215 : :
216 : : void
217 : 421537 : spdk_rpc_register_method(const char *method, spdk_rpc_method_handler func, uint32_t state_mask)
218 : : {
219 : 2 : struct spdk_rpc_method *m;
220 : :
221 : 421537 : m = _get_rpc_method_raw(method);
222 [ - + ]: 421537 : 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 : 421537 : m = calloc(1, sizeof(struct spdk_rpc_method));
229 [ + + # # ]: 421537 : assert(m != NULL);
230 : :
231 [ + + + - : 421537 : m->name = strdup(method);
+ - ]
232 [ + + + - : 421537 : assert(m->name != NULL);
+ - # # ]
233 : :
234 [ + - + - ]: 421537 : m->func = func;
235 [ + - + - ]: 421537 : m->state_mask = state_mask;
236 : :
237 : : /* TODO: use a hash table or sorted list */
238 [ + - + - : 421537 : SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
+ - ]
239 [ - + ]: 13870 : }
240 : :
241 : : void
242 : 15602 : spdk_rpc_register_alias_deprecated(const char *method, const char *alias)
243 : : {
244 : 0 : struct spdk_rpc_method *m, *base;
245 : :
246 : 15602 : base = _get_rpc_method_raw(method);
247 [ + + ]: 15602 : 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 [ + + + - : 15602 : 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 : 15602 : m = calloc(1, sizeof(struct spdk_rpc_method));
261 [ + + # # ]: 15602 : assert(m != NULL);
262 : :
263 [ + + + - : 15602 : m->name = strdup(alias);
+ - ]
264 [ + + + - : 15602 : assert(m->name != NULL);
+ - # # ]
265 : :
266 [ + - + - ]: 15602 : m->is_alias_of = base;
267 [ + - + - ]: 15602 : m->is_deprecated = true;
268 [ + - + - : 15602 : m->state_mask = base->state_mask;
+ - + - ]
269 : :
270 : : /* TODO: use a hash table or sorted list */
271 [ + - + - : 15602 : SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
+ - ]
272 [ # # ]: 512 : }
273 : :
274 : : bool
275 : 4329 : spdk_rpc_verify_methods(void)
276 : : {
277 [ + + ]: 4329 : return g_rpcs_correct;
278 : : }
279 : :
280 : : int
281 : 18 : spdk_rpc_is_method_allowed(const char *method, uint32_t state_mask)
282 : : {
283 : 3 : 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 : 3 : }
303 : :
304 : : int
305 : 8608 : spdk_rpc_get_method_state_mask(const char *method, uint32_t *state_mask)
306 : : {
307 : 2 : struct spdk_rpc_method *m;
308 : :
309 [ + + + - : 829702 : SLIST_FOREACH(m, &g_rpc_methods, slist) {
+ - + - ]
310 [ + + + + : 829696 : if (strcmp(m->name, method) == 0) {
+ + + - +
+ ]
311 [ - + - + : 8602 : *state_mask = m->state_mask;
- + ]
312 : 8602 : return 0;
313 : : }
314 : 63142 : }
315 : :
316 : 6 : return -ENOENT;
317 : 596 : }
318 : :
319 : : void
320 : 2901 : spdk_rpc_set_allowlist(const char **rpc_allowlist)
321 : : {
322 : 2901 : spdk_strarray_free(g_rpcs_allowlist);
323 : :
324 [ + + ]: 2901 : if (rpc_allowlist == NULL) {
325 : 2880 : g_rpcs_allowlist = NULL;
326 : 2880 : return;
327 : : }
328 : :
329 : 21 : g_rpcs_allowlist = spdk_strarray_dup(rpc_allowlist);
330 [ + + # # ]: 21 : assert(g_rpcs_allowlist != NULL);
331 : 105 : }
332 : :
333 : : void
334 : 4594 : spdk_rpc_close(void)
335 : : {
336 [ + + ]: 4594 : if (g_jsonrpc_server) {
337 [ + - + - : 4309 : if (g_rpc_listen_addr_unix.sun_path[0]) {
+ - ]
338 : : /* Delete the Unix socket file */
339 [ + + ]: 4309 : unlink(g_rpc_listen_addr_unix.sun_path);
340 [ + - + - ]: 4309 : g_rpc_listen_addr_unix.sun_path[0] = '\0';
341 : 184 : }
342 : :
343 : 4309 : spdk_jsonrpc_server_shutdown(g_jsonrpc_server);
344 : 4309 : g_jsonrpc_server = NULL;
345 : :
346 [ + - ]: 4309 : if (g_rpc_lock_fd != -1) {
347 : 4309 : close(g_rpc_lock_fd);
348 : 4309 : g_rpc_lock_fd = -1;
349 : 184 : }
350 : :
351 [ + - + - : 4309 : if (g_rpc_lock_path[0]) {
+ - ]
352 [ + + ]: 4309 : unlink(g_rpc_lock_path);
353 [ + - + - ]: 4309 : g_rpc_lock_path[0] = '\0';
354 : 184 : }
355 : 184 : }
356 : 4594 : }
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 : 1974 : rpc_get_methods(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
370 : : {
371 : 1974 : struct rpc_get_methods req = {};
372 : 2 : struct spdk_json_write_ctx *w;
373 : 2 : struct spdk_rpc_method *m;
374 : :
375 [ + + ]: 1974 : if (params != NULL) {
376 [ + + ]: 559 : 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 : 3 : }
384 : :
385 : 1968 : w = spdk_jsonrpc_begin_result(request);
386 : 1968 : spdk_json_write_array_begin(w);
387 [ + + + - : 393942 : SLIST_FOREACH(m, &g_rpc_methods, slist) {
+ - + - ]
388 [ + + + - : 391974 : if (!rpc_is_allowed(m->name)) {
+ + ]
389 : 10038 : continue;
390 : : }
391 [ + + + + : 381936 : if (m->is_alias_of != NULL && !req.include_aliases) {
+ + + - +
+ ]
392 : 10734 : continue;
393 : : }
394 [ + + + + : 371202 : if (req.current && ((m->state_mask & g_rpc_state) != g_rpc_state)) {
+ + - + +
+ ]
395 : 9967 : continue;
396 : : }
397 [ + - + - ]: 361235 : spdk_json_write_string(w, m->name);
398 : 5958 : }
399 : 1968 : spdk_json_write_array_end(w);
400 : 1968 : spdk_jsonrpc_end_result(request, w);
401 [ - + ]: 33 : }
402 : 3535 : 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 : 2 : 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 [ - + ]: 3 : }
432 : 3535 : SPDK_RPC_REGISTER("spdk_get_version", rpc_spdk_get_version,
433 : : SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
|