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