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