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