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 : 4530 : spdk_rpc_set_state(uint32_t state)
42 : : {
43 : 4530 : g_rpc_state = state;
44 : 4530 : }
45 : :
46 : : uint32_t
47 : 484985 : spdk_rpc_get_state(void)
48 : : {
49 : 484985 : return g_rpc_state;
50 : : }
51 : :
52 : : static bool
53 : 455181 : rpc_is_allowed(const char *name)
54 : : {
55 : : size_t i;
56 : :
57 [ + + ]: 455181 : if (g_rpcs_allowlist == NULL) {
58 : 445007 : 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 : 491843 : _get_rpc_method(const struct spdk_json_val *method)
73 : : {
74 : : struct spdk_rpc_method *m;
75 : :
76 [ + + ]: 43420349 : SLIST_FOREACH(m, &g_rpc_methods, slist) {
77 [ + + ]: 42984983 : if (spdk_json_strequal(method, m->name)) {
78 [ + + ]: 56485 : if (!rpc_is_allowed(m->name)) {
79 : 21 : return NULL;
80 : : }
81 : 56464 : return m;
82 : : }
83 : : }
84 : :
85 : 435358 : return NULL;
86 : : }
87 : :
88 : : static struct spdk_rpc_method *
89 : 451789 : _get_rpc_method_raw(const char *method)
90 : : {
91 : 214918 : struct spdk_json_val method_val;
92 : :
93 : 451789 : method_val.type = SPDK_JSON_VAL_STRING;
94 [ - + ]: 451789 : method_val.len = strlen(method);
95 : 451789 : method_val.start = (char *)method;
96 : :
97 : 451789 : return _get_rpc_method(&method_val);
98 : : }
99 : :
100 : : static void
101 : 40054 : 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 [ - + ]: 40054 : assert(method != NULL);
108 : :
109 : 40054 : m = _get_rpc_method(method);
110 [ + + ]: 40054 : 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 [ + + ]: 40027 : 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 [ + + ]: 40027 : if ((m->state_mask & g_rpc_state) == g_rpc_state) {
124 : 40015 : 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 : 4419 : spdk_rpc_listen(const char *listen_addr)
146 : : {
147 : : int rc;
148 : :
149 : 4419 : memset(&g_rpc_listen_addr_unix, 0, sizeof(g_rpc_listen_addr_unix));
150 : :
151 : 4419 : g_rpc_listen_addr_unix.sun_family = AF_UNIX;
152 : 4419 : rc = snprintf(g_rpc_listen_addr_unix.sun_path,
153 : : sizeof(g_rpc_listen_addr_unix.sun_path),
154 : : "%s", listen_addr);
155 [ + - - + ]: 4419 : 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 : 4419 : rc = snprintf(g_rpc_lock_path, sizeof(g_rpc_lock_path), "%s.lock",
162 : : g_rpc_listen_addr_unix.sun_path);
163 [ + - - + ]: 4419 : 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 : 4419 : g_rpc_lock_fd = open(g_rpc_lock_path, O_RDWR | O_CREAT, 0600);
171 [ - + ]: 4419 : 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 : 4419 : rc = flock(g_rpc_lock_fd, LOCK_EX | LOCK_NB);
180 [ + + ]: 4419 : 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 : 4372 : unlink(g_rpc_listen_addr_unix.sun_path);
193 : :
194 : 4372 : 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 [ - + ]: 4372 : 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 : 4372 : return 0;
208 : : }
209 : :
210 : : void
211 : 3925626 : spdk_rpc_accept(void)
212 : : {
213 : 3925626 : spdk_jsonrpc_server_poll(g_jsonrpc_server);
214 : 3925626 : }
215 : :
216 : : void
217 : 435352 : 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 : 435352 : m = _get_rpc_method_raw(method);
222 [ - + ]: 435352 : 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 : 435352 : m = calloc(1, sizeof(struct spdk_rpc_method));
229 [ - + ]: 435352 : assert(m != NULL);
230 : :
231 [ - + ]: 435352 : m->name = strdup(method);
232 [ - + ]: 435352 : assert(m->name != NULL);
233 : :
234 : 435352 : m->func = func;
235 : 435352 : m->state_mask = state_mask;
236 : :
237 : : /* TODO: use a hash table or sorted list */
238 : 435352 : SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
239 : : }
240 : :
241 : : void
242 : 16437 : spdk_rpc_register_alias_deprecated(const char *method, const char *alias)
243 : : {
244 : : struct spdk_rpc_method *m, *base;
245 : :
246 : 16437 : base = _get_rpc_method_raw(method);
247 [ - + ]: 16437 : 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 [ - + ]: 16437 : 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 : 16437 : m = calloc(1, sizeof(struct spdk_rpc_method));
261 [ - + ]: 16437 : assert(m != NULL);
262 : :
263 [ - + ]: 16437 : m->name = strdup(alias);
264 [ - + ]: 16437 : assert(m->name != NULL);
265 : :
266 : 16437 : m->is_alias_of = base;
267 : 16437 : m->is_deprecated = true;
268 : 16437 : m->state_mask = base->state_mask;
269 : :
270 : : /* TODO: use a hash table or sorted list */
271 : 16437 : SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
272 : : }
273 : :
274 : : bool
275 : 4392 : spdk_rpc_verify_methods(void)
276 : : {
277 [ - + ]: 4392 : 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 : 2969 : spdk_rpc_set_allowlist(const char **rpc_allowlist)
321 : : {
322 : 2969 : spdk_strarray_free(g_rpcs_allowlist);
323 : :
324 [ + + ]: 2969 : if (rpc_allowlist == NULL) {
325 : 2948 : g_rpcs_allowlist = NULL;
326 : 2948 : 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 : 4670 : spdk_rpc_close(void)
335 : : {
336 [ + + ]: 4670 : if (g_jsonrpc_server) {
337 [ + - ]: 4372 : if (g_rpc_listen_addr_unix.sun_path[0]) {
338 : : /* Delete the Unix socket file */
339 [ - + ]: 4372 : unlink(g_rpc_listen_addr_unix.sun_path);
340 : 4372 : g_rpc_listen_addr_unix.sun_path[0] = '\0';
341 : : }
342 : :
343 : 4372 : spdk_jsonrpc_server_shutdown(g_jsonrpc_server);
344 : 4372 : g_jsonrpc_server = NULL;
345 : :
346 [ + - ]: 4372 : if (g_rpc_lock_fd != -1) {
347 : 4372 : close(g_rpc_lock_fd);
348 : 4372 : g_rpc_lock_fd = -1;
349 : : }
350 : :
351 [ + - ]: 4372 : if (g_rpc_lock_path[0]) {
352 [ - + ]: 4372 : unlink(g_rpc_lock_path);
353 : 4372 : g_rpc_lock_path[0] = '\0';
354 : : }
355 : : }
356 : 4670 : }
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 : 2013 : rpc_get_methods(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
370 : : {
371 : 2013 : struct rpc_get_methods req = {};
372 : : struct spdk_json_write_ctx *w;
373 : : struct spdk_rpc_method *m;
374 : :
375 [ + + ]: 2013 : 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 : 2007 : w = spdk_jsonrpc_begin_result(request);
386 : 2007 : spdk_json_write_array_begin(w);
387 [ + + ]: 400685 : SLIST_FOREACH(m, &g_rpc_methods, slist) {
388 [ + + ]: 398678 : if (!rpc_is_allowed(m->name)) {
389 : 10006 : continue;
390 : : }
391 [ + + + + : 388672 : if (m->is_alias_of != NULL && !req.include_aliases) {
+ + ]
392 : 11097 : continue;
393 : : }
394 [ + + + + : 377575 : if (req.current && ((m->state_mask & g_rpc_state) != g_rpc_state)) {
+ + ]
395 : 10165 : continue;
396 : : }
397 : 367410 : spdk_json_write_string(w, m->name);
398 : : }
399 : 2007 : spdk_json_write_array_end(w);
400 : 2007 : spdk_jsonrpc_end_result(request, w);
401 : : }
402 : 3621 : 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 : 3621 : SPDK_RPC_REGISTER("spdk_get_version", rpc_spdk_get_version,
433 : : SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
|