xref: /spdk/lib/rpc/rpc.c (revision 488570ebd418ba07c9e69e65106dcc964f3bb41b)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (c) Intel Corporation. All rights reserved.
3  *   Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved.
4  */
5 
6 #include <sys/file.h>
7 
8 #include "spdk/stdinc.h"
9 
10 #include "spdk/queue.h"
11 #include "spdk/rpc.h"
12 #include "spdk/env.h"
13 #include "spdk/log.h"
14 #include "spdk/string.h"
15 #include "spdk/util.h"
16 #include "spdk/version.h"
17 
18 static struct sockaddr_un g_rpc_listen_addr_unix = {};
19 static char g_rpc_lock_path[sizeof(g_rpc_listen_addr_unix.sun_path) + sizeof(".lock")];
20 static int g_rpc_lock_fd = -1;
21 
22 static struct spdk_jsonrpc_server *g_jsonrpc_server = NULL;
23 static uint32_t g_rpc_state;
24 static bool g_rpcs_correct = true;
25 
26 struct spdk_rpc_method {
27 	const char *name;
28 	spdk_rpc_method_handler func;
29 	SLIST_ENTRY(spdk_rpc_method) slist;
30 	uint32_t state_mask;
31 	bool is_deprecated;
32 	struct spdk_rpc_method *is_alias_of;
33 	bool deprecation_warning_printed;
34 };
35 
36 static SLIST_HEAD(, spdk_rpc_method) g_rpc_methods = SLIST_HEAD_INITIALIZER(g_rpc_methods);
37 
38 void
39 spdk_rpc_set_state(uint32_t state)
40 {
41 	g_rpc_state = state;
42 }
43 
44 uint32_t
45 spdk_rpc_get_state(void)
46 {
47 	return g_rpc_state;
48 }
49 
50 static struct spdk_rpc_method *
51 _get_rpc_method(const struct spdk_json_val *method)
52 {
53 	struct spdk_rpc_method *m;
54 
55 	SLIST_FOREACH(m, &g_rpc_methods, slist) {
56 		if (spdk_json_strequal(method, m->name)) {
57 			return m;
58 		}
59 	}
60 
61 	return NULL;
62 }
63 
64 static struct spdk_rpc_method *
65 _get_rpc_method_raw(const char *method)
66 {
67 	struct spdk_json_val method_val;
68 
69 	method_val.type = SPDK_JSON_VAL_STRING;
70 	method_val.len = strlen(method);
71 	method_val.start = (char *)method;
72 
73 	return _get_rpc_method(&method_val);
74 }
75 
76 static void
77 jsonrpc_handler(struct spdk_jsonrpc_request *request,
78 		const struct spdk_json_val *method,
79 		const struct spdk_json_val *params)
80 {
81 	struct spdk_rpc_method *m;
82 
83 	assert(method != NULL);
84 
85 	m = _get_rpc_method(method);
86 	if (m == NULL) {
87 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND, "Method not found");
88 		return;
89 	}
90 
91 	if (m->is_alias_of != NULL) {
92 		if (m->is_deprecated && !m->deprecation_warning_printed) {
93 			SPDK_WARNLOG("RPC method %s is deprecated.  Use %s instead.\n", m->name, m->is_alias_of->name);
94 			m->deprecation_warning_printed = true;
95 		}
96 		m = m->is_alias_of;
97 	}
98 
99 	if ((m->state_mask & g_rpc_state) == g_rpc_state) {
100 		m->func(request, params);
101 	} else {
102 		if (g_rpc_state == SPDK_RPC_STARTUP) {
103 			spdk_jsonrpc_send_error_response_fmt(request,
104 							     SPDK_JSONRPC_ERROR_INVALID_STATE,
105 							     "Method may only be called after "
106 							     "framework is initialized "
107 							     "using framework_start_init RPC.");
108 		} else {
109 			spdk_jsonrpc_send_error_response_fmt(request,
110 							     SPDK_JSONRPC_ERROR_INVALID_STATE,
111 							     "Method may only be called before "
112 							     "framework is initialized. "
113 							     "Use --wait-for-rpc command line "
114 							     "parameter and then issue this RPC "
115 							     "before the framework_start_init RPC.");
116 		}
117 	}
118 }
119 
120 int
121 spdk_rpc_listen(const char *listen_addr)
122 {
123 	int rc;
124 
125 	memset(&g_rpc_listen_addr_unix, 0, sizeof(g_rpc_listen_addr_unix));
126 
127 	g_rpc_listen_addr_unix.sun_family = AF_UNIX;
128 	rc = snprintf(g_rpc_listen_addr_unix.sun_path,
129 		      sizeof(g_rpc_listen_addr_unix.sun_path),
130 		      "%s", listen_addr);
131 	if (rc < 0 || (size_t)rc >= sizeof(g_rpc_listen_addr_unix.sun_path)) {
132 		SPDK_ERRLOG("RPC Listen address Unix socket path too long\n");
133 		g_rpc_listen_addr_unix.sun_path[0] = '\0';
134 		return -1;
135 	}
136 
137 	rc = snprintf(g_rpc_lock_path, sizeof(g_rpc_lock_path), "%s.lock",
138 		      g_rpc_listen_addr_unix.sun_path);
139 	if (rc < 0 || (size_t)rc >= sizeof(g_rpc_lock_path)) {
140 		SPDK_ERRLOG("RPC lock path too long\n");
141 		g_rpc_listen_addr_unix.sun_path[0] = '\0';
142 		g_rpc_lock_path[0] = '\0';
143 		return -1;
144 	}
145 
146 	g_rpc_lock_fd = open(g_rpc_lock_path, O_RDONLY | O_CREAT, 0600);
147 	if (g_rpc_lock_fd == -1) {
148 		SPDK_ERRLOG("Cannot open lock file %s: %s\n",
149 			    g_rpc_lock_path, spdk_strerror(errno));
150 		g_rpc_listen_addr_unix.sun_path[0] = '\0';
151 		g_rpc_lock_path[0] = '\0';
152 		return -1;
153 	}
154 
155 	rc = flock(g_rpc_lock_fd, LOCK_EX | LOCK_NB);
156 	if (rc != 0) {
157 		SPDK_ERRLOG("RPC Unix domain socket path %s in use. Specify another.\n",
158 			    g_rpc_listen_addr_unix.sun_path);
159 		g_rpc_listen_addr_unix.sun_path[0] = '\0';
160 		g_rpc_lock_path[0] = '\0';
161 		return -1;
162 	}
163 
164 	/*
165 	 * Since we acquired the lock, it is safe to delete the Unix socket file
166 	 * if it still exists from a previous process.
167 	 */
168 	unlink(g_rpc_listen_addr_unix.sun_path);
169 
170 	g_jsonrpc_server = spdk_jsonrpc_server_listen(AF_UNIX, 0,
171 			   (struct sockaddr *)&g_rpc_listen_addr_unix,
172 			   sizeof(g_rpc_listen_addr_unix),
173 			   jsonrpc_handler);
174 	if (g_jsonrpc_server == NULL) {
175 		SPDK_ERRLOG("spdk_jsonrpc_server_listen() failed\n");
176 		close(g_rpc_lock_fd);
177 		g_rpc_lock_fd = -1;
178 		unlink(g_rpc_lock_path);
179 		g_rpc_lock_path[0] = '\0';
180 		return -1;
181 	}
182 
183 	return 0;
184 }
185 
186 void
187 spdk_rpc_accept(void)
188 {
189 	spdk_jsonrpc_server_poll(g_jsonrpc_server);
190 }
191 
192 void
193 spdk_rpc_register_method(const char *method, spdk_rpc_method_handler func, uint32_t state_mask)
194 {
195 	struct spdk_rpc_method *m;
196 
197 	m = _get_rpc_method_raw(method);
198 	if (m != NULL) {
199 		SPDK_ERRLOG("duplicate RPC %s registered...\n", method);
200 		g_rpcs_correct = false;
201 		return;
202 	}
203 
204 	m = calloc(1, sizeof(struct spdk_rpc_method));
205 	assert(m != NULL);
206 
207 	m->name = strdup(method);
208 	assert(m->name != NULL);
209 
210 	m->func = func;
211 	m->state_mask = state_mask;
212 
213 	/* TODO: use a hash table or sorted list */
214 	SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
215 }
216 
217 void
218 spdk_rpc_register_alias_deprecated(const char *method, const char *alias)
219 {
220 	struct spdk_rpc_method *m, *base;
221 
222 	base = _get_rpc_method_raw(method);
223 	if (base == NULL) {
224 		SPDK_ERRLOG("cannot create alias %s - method %s does not exist\n",
225 			    alias, method);
226 		g_rpcs_correct = false;
227 		return;
228 	}
229 
230 	if (base->is_alias_of != NULL) {
231 		SPDK_ERRLOG("cannot create alias %s of alias %s\n", alias, method);
232 		g_rpcs_correct = false;
233 		return;
234 	}
235 
236 	m = calloc(1, sizeof(struct spdk_rpc_method));
237 	assert(m != NULL);
238 
239 	m->name = strdup(alias);
240 	assert(m->name != NULL);
241 
242 	m->is_alias_of = base;
243 	m->is_deprecated = true;
244 	m->state_mask = base->state_mask;
245 
246 	/* TODO: use a hash table or sorted list */
247 	SLIST_INSERT_HEAD(&g_rpc_methods, m, slist);
248 }
249 
250 bool
251 spdk_rpc_verify_methods(void)
252 {
253 	return g_rpcs_correct;
254 }
255 
256 int
257 spdk_rpc_is_method_allowed(const char *method, uint32_t state_mask)
258 {
259 	struct spdk_rpc_method *m;
260 
261 	SLIST_FOREACH(m, &g_rpc_methods, slist) {
262 		if (strcmp(m->name, method) != 0) {
263 			continue;
264 		}
265 
266 		if ((m->state_mask & state_mask) == state_mask) {
267 			return 0;
268 		} else {
269 			return -EPERM;
270 		}
271 	}
272 
273 	return -ENOENT;
274 }
275 
276 void
277 spdk_rpc_close(void)
278 {
279 	if (g_jsonrpc_server) {
280 		if (g_rpc_listen_addr_unix.sun_path[0]) {
281 			/* Delete the Unix socket file */
282 			unlink(g_rpc_listen_addr_unix.sun_path);
283 			g_rpc_listen_addr_unix.sun_path[0] = '\0';
284 		}
285 
286 		spdk_jsonrpc_server_shutdown(g_jsonrpc_server);
287 		g_jsonrpc_server = NULL;
288 
289 		if (g_rpc_lock_fd != -1) {
290 			close(g_rpc_lock_fd);
291 			g_rpc_lock_fd = -1;
292 		}
293 
294 		if (g_rpc_lock_path[0]) {
295 			unlink(g_rpc_lock_path);
296 			g_rpc_lock_path[0] = '\0';
297 		}
298 	}
299 }
300 
301 struct rpc_get_methods {
302 	bool current;
303 	bool include_aliases;
304 };
305 
306 static const struct spdk_json_object_decoder rpc_get_methods_decoders[] = {
307 	{"current", offsetof(struct rpc_get_methods, current), spdk_json_decode_bool, true},
308 	{"include_aliases", offsetof(struct rpc_get_methods, include_aliases), spdk_json_decode_bool, true},
309 };
310 
311 static void
312 rpc_get_methods(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
313 {
314 	struct rpc_get_methods req = {};
315 	struct spdk_json_write_ctx *w;
316 	struct spdk_rpc_method *m;
317 
318 	if (params != NULL) {
319 		if (spdk_json_decode_object(params, rpc_get_methods_decoders,
320 					    SPDK_COUNTOF(rpc_get_methods_decoders), &req)) {
321 			SPDK_ERRLOG("spdk_json_decode_object failed\n");
322 			spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
323 							 "Invalid parameters");
324 			return;
325 		}
326 	}
327 
328 	w = spdk_jsonrpc_begin_result(request);
329 	spdk_json_write_array_begin(w);
330 	SLIST_FOREACH(m, &g_rpc_methods, slist) {
331 		if (m->is_alias_of != NULL && !req.include_aliases) {
332 			continue;
333 		}
334 		if (req.current && ((m->state_mask & g_rpc_state) != g_rpc_state)) {
335 			continue;
336 		}
337 		spdk_json_write_string(w, m->name);
338 	}
339 	spdk_json_write_array_end(w);
340 	spdk_jsonrpc_end_result(request, w);
341 }
342 SPDK_RPC_REGISTER("rpc_get_methods", rpc_get_methods, SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
343 
344 static void
345 rpc_spdk_get_version(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
346 {
347 	struct spdk_json_write_ctx *w;
348 
349 	if (params != NULL) {
350 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
351 						 "spdk_get_version method requires no parameters");
352 		return;
353 	}
354 
355 	w = spdk_jsonrpc_begin_result(request);
356 	spdk_json_write_object_begin(w);
357 
358 	spdk_json_write_named_string_fmt(w, "version", "%s", SPDK_VERSION_STRING);
359 	spdk_json_write_named_object_begin(w, "fields");
360 	spdk_json_write_named_uint32(w, "major", SPDK_VERSION_MAJOR);
361 	spdk_json_write_named_uint32(w, "minor", SPDK_VERSION_MINOR);
362 	spdk_json_write_named_uint32(w, "patch", SPDK_VERSION_PATCH);
363 	spdk_json_write_named_string_fmt(w, "suffix", "%s", SPDK_VERSION_SUFFIX);
364 #ifdef SPDK_GIT_COMMIT
365 	spdk_json_write_named_string_fmt(w, "commit", "%s", SPDK_GIT_COMMIT_STRING);
366 #endif
367 	spdk_json_write_object_end(w);
368 
369 	spdk_json_write_object_end(w);
370 	spdk_jsonrpc_end_result(request, w);
371 }
372 SPDK_RPC_REGISTER("spdk_get_version", rpc_spdk_get_version,
373 		  SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
374