xref: /spdk/test/rpc_client/rpc_client_test.c (revision 712a3f69d32632bf6c862f00200f7f437d3f7529)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 #include "spdk/event.h"
36 #include "spdk/jsonrpc.h"
37 #include "spdk/util.h"
38 #include "spdk/rpc.h"
39 
40 
41 #define RPC_MAX_METHODS 200
42 #define JOIN_TIMEOUT_S 1
43 
44 static const char *g_rpcsock_addr = SPDK_DEFAULT_RPC_ADDR;
45 static int g_addr_family = AF_UNIX;
46 
47 #define RPC_MAX_METHODS 200
48 
49 struct get_jsonrpc_methods_resp {
50 	char *method_names[RPC_MAX_METHODS];
51 	size_t method_num;
52 };
53 
54 static int
55 _rpc_client_wait_for_response(struct spdk_jsonrpc_client *client)
56 {
57 	int rc;
58 
59 	do {
60 		rc = spdk_jsonrpc_client_poll(client, 1);
61 	} while (rc == 0 || rc == -ENOTCONN);
62 
63 	if (rc <= 0) {
64 		SPDK_ERRLOG("Failed to get response: %d\n", rc);
65 	}
66 
67 	return rc;
68 }
69 
70 static int
71 get_jsonrpc_method_json_parser(struct get_jsonrpc_methods_resp *resp,
72 			       const struct spdk_json_val *result)
73 {
74 	return spdk_json_decode_array(result, spdk_json_decode_string, resp->method_names,
75 				      RPC_MAX_METHODS, &resp->method_num, sizeof(char *));
76 }
77 
78 static int
79 spdk_jsonrpc_client_check_rpc_method(struct spdk_jsonrpc_client *client, char *method_name)
80 {
81 	int rc, i;
82 	struct spdk_jsonrpc_client_response *json_resp = NULL;
83 	struct get_jsonrpc_methods_resp resp = {};
84 	struct spdk_json_write_ctx *w;
85 	struct spdk_jsonrpc_client_request *request;
86 
87 	request = spdk_jsonrpc_client_create_request();
88 	if (request == NULL) {
89 		return -ENOMEM;
90 	}
91 
92 	w = spdk_jsonrpc_begin_request(request, 1, "rpc_get_methods");
93 	spdk_jsonrpc_end_request(request, w);
94 	spdk_jsonrpc_client_send_request(client, request);
95 
96 	rc = _rpc_client_wait_for_response(client);
97 	if (rc <= 0) {
98 		goto out;
99 	}
100 
101 	json_resp = spdk_jsonrpc_client_get_response(client);
102 	if (json_resp == NULL) {
103 		SPDK_ERRLOG("spdk_jsonrpc_client_get_response() failed\n");
104 		rc = -1;
105 		goto out;
106 
107 	}
108 
109 	/* Check for error response */
110 	if (json_resp->error != NULL) {
111 		SPDK_ERRLOG("Unexpected error response\n");
112 		rc = -1;
113 		goto out;
114 	}
115 
116 	assert(json_resp->result);
117 
118 	rc = get_jsonrpc_method_json_parser(&resp, json_resp->result);
119 	if (rc) {
120 		SPDK_ERRLOG("get_jsonrpc_method_json_parser() failed\n");
121 		goto out;
122 	}
123 
124 	for (i = 0; i < (int)resp.method_num; i++) {
125 		if (strcmp(method_name, resp.method_names[i]) == 0) {
126 			rc = 0;
127 			goto out;
128 		}
129 	}
130 
131 	rc = -1;
132 	SPDK_ERRLOG("Method '%s' not found in response\n", method_name);
133 
134 out:
135 	for (i = 0; i < (int)resp.method_num; i++) {
136 		SPDK_NOTICELOG("%s\n", resp.method_names[i]);
137 		free(resp.method_names[i]);
138 	}
139 
140 	spdk_jsonrpc_client_free_response(json_resp);
141 	return rc;
142 }
143 
144 static int
145 spdk_jsonrpc_client_check_null_params_method(struct spdk_jsonrpc_client *client)
146 {
147 	int rc;
148 	bool res = false;
149 	struct spdk_jsonrpc_client_response *json_resp = NULL;
150 	struct spdk_json_write_ctx *w;
151 	struct spdk_jsonrpc_client_request *request;
152 
153 	request = spdk_jsonrpc_client_create_request();
154 	if (request == NULL) {
155 		return -ENOMEM;
156 	}
157 
158 	w = spdk_jsonrpc_begin_request(request, 1, "test_null_params");
159 	spdk_json_write_name(w, "params");
160 	spdk_json_write_null(w);
161 	spdk_jsonrpc_end_request(request, w);
162 	spdk_jsonrpc_client_send_request(client, request);
163 
164 	rc = _rpc_client_wait_for_response(client);
165 	if (rc <= 0) {
166 		goto out;
167 	}
168 
169 	json_resp = spdk_jsonrpc_client_get_response(client);
170 	if (json_resp == NULL) {
171 		SPDK_ERRLOG("spdk_jsonrpc_client_get_response() failed\n");
172 		rc = -1;
173 		goto out;
174 
175 	}
176 
177 	/* Check for error response */
178 	if (json_resp->error != NULL) {
179 		SPDK_ERRLOG("Unexpected error response\n");
180 		rc = -1;
181 		goto out;
182 	}
183 
184 	assert(json_resp->result);
185 
186 	if (spdk_json_decode_bool(json_resp->result, &res) != 0 || res != true) {
187 		SPDK_ERRLOG("Response is not a boolean or it is not 'true'\n");
188 		rc = -EINVAL;
189 		goto out;
190 	} else {
191 		rc = 0;
192 	}
193 
194 out:
195 	spdk_jsonrpc_client_free_response(json_resp);
196 	return rc;
197 }
198 
199 static void
200 rpc_test_method_startup(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
201 {
202 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
203 					 "rpc_test_method_startup(): Method body not implemented");
204 }
205 SPDK_RPC_REGISTER("test_method_startup", rpc_test_method_startup, SPDK_RPC_STARTUP)
206 
207 static void
208 rpc_test_method_runtime(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
209 {
210 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
211 					 "rpc_test_method_runtime(): Method body not implemented");
212 }
213 SPDK_RPC_REGISTER("test_method_runtime", rpc_test_method_runtime, SPDK_RPC_RUNTIME)
214 
215 static void
216 rpc_test_method_null_params(struct spdk_jsonrpc_request *request,
217 			    const struct spdk_json_val *params)
218 {
219 	struct spdk_json_write_ctx *w;
220 
221 	if (params != NULL) {
222 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
223 						 "rpc_test_method_null_params(): Parameters are not NULL");
224 		return;
225 	}
226 	w = spdk_jsonrpc_begin_result(request);
227 	assert(w != NULL);
228 	spdk_json_write_bool(w, true);
229 	spdk_jsonrpc_end_result(request, w);
230 }
231 SPDK_RPC_REGISTER("test_null_params", rpc_test_method_null_params, SPDK_RPC_RUNTIME)
232 
233 static bool g_conn_close_detected;
234 
235 static void
236 rpc_test_conn_close_cb(struct spdk_jsonrpc_server_conn *conn, void *ctx)
237 {
238 	assert((intptr_t)ctx == 42);
239 	g_conn_close_detected = true;
240 }
241 
242 static void
243 rpc_hook_conn_close(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
244 {
245 	struct spdk_jsonrpc_server_conn *conn = spdk_jsonrpc_get_conn(request);
246 	struct spdk_json_write_ctx *w;
247 	int rc;
248 
249 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
250 	if (rc != 0) {
251 
252 		rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
253 		assert(rc == -ENOSPC);
254 	}
255 
256 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
257 	if (rc != -EEXIST) {
258 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
259 						 "rpc_test_method_conn_close_detect(): rc != -EEXIST");
260 		return;
261 	}
262 
263 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(43));
264 	if (rc != -ENOSPC) {
265 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
266 						 "rpc_test_method_conn_close_detect(): rc != -ENOSPC");
267 		return;
268 	}
269 
270 	w = spdk_jsonrpc_begin_result(request);
271 	assert(w != NULL);
272 	spdk_json_write_bool(w, true);
273 	spdk_jsonrpc_end_result(request, w);
274 
275 }
276 SPDK_RPC_REGISTER("hook_conn_close", rpc_hook_conn_close, SPDK_RPC_RUNTIME | SPDK_RPC_STARTUP)
277 
278 static int
279 spdk_jsonrpc_client_hook_conn_close(struct spdk_jsonrpc_client *client)
280 {
281 	int rc;
282 	bool res = false;
283 	struct spdk_jsonrpc_client_response *json_resp = NULL;
284 	struct spdk_json_write_ctx *w;
285 	struct spdk_jsonrpc_client_request *request;
286 
287 	request = spdk_jsonrpc_client_create_request();
288 	if (request == NULL) {
289 		return -ENOMEM;
290 	}
291 
292 	w = spdk_jsonrpc_begin_request(request, 1, "hook_conn_close");
293 	spdk_jsonrpc_end_request(request, w);
294 	spdk_jsonrpc_client_send_request(client, request);
295 
296 	rc = _rpc_client_wait_for_response(client);
297 	if (rc <= 0) {
298 		goto out;
299 	}
300 
301 	json_resp = spdk_jsonrpc_client_get_response(client);
302 	if (json_resp == NULL) {
303 		SPDK_ERRLOG("spdk_jsonrpc_client_get_response() failed\n");
304 		rc = -errno;
305 		goto out;
306 
307 	}
308 
309 	/* Check for error response */
310 	if (json_resp->error != NULL) {
311 		SPDK_ERRLOG("Unexpected error response: %*s\n", json_resp->error->len,
312 			    (char *)json_resp->error->start);
313 		rc = -EIO;
314 		goto out;
315 	}
316 
317 	assert(json_resp->result);
318 	if (spdk_json_decode_bool(json_resp->result, &res) != 0 || res != true) {
319 		SPDK_ERRLOG("Response is not and boolean or if not 'true'\n");
320 		rc = -EINVAL;
321 		goto out;
322 	}
323 
324 	rc = 0;
325 out:
326 	spdk_jsonrpc_client_free_response(json_resp);
327 	return rc;
328 }
329 
330 volatile int g_rpc_server_th_stop;
331 static sem_t g_rpc_server_th_listening;
332 
333 static void *
334 rpc_server_th(void *arg)
335 {
336 	int rc;
337 
338 	rc = spdk_rpc_listen(g_rpcsock_addr);
339 	if (rc) {
340 		fprintf(stderr, "spdk_rpc_listen() failed: %d\n", rc);
341 		sem_post(&g_rpc_server_th_listening);
342 		goto out;
343 	}
344 
345 	sem_post(&g_rpc_server_th_listening);
346 
347 	while (!g_rpc_server_th_stop) {
348 		spdk_rpc_accept();
349 		usleep(50);
350 	}
351 
352 	spdk_rpc_close();
353 out:
354 	return (void *)(intptr_t)rc;
355 }
356 
357 static void *
358 rpc_client_th(void *arg)
359 {
360 	struct spdk_jsonrpc_client *client = NULL;
361 	char *method_name = "rpc_get_methods";
362 	int rc;
363 
364 
365 	rc = sem_wait(&g_rpc_server_th_listening);
366 	if (rc == -1) {
367 		fprintf(stderr, "Timeout waiting for server thread to start listening: rc=%d errno=%d\n", rc,
368 			errno);
369 		goto out;
370 	}
371 
372 	client = spdk_jsonrpc_client_connect(g_rpcsock_addr, g_addr_family);
373 	if (!client) {
374 		fprintf(stderr, "spdk_jsonrpc_client_connect() failed: %d\n", errno);
375 		rc = -1;
376 		goto out;
377 	}
378 
379 	rc = spdk_jsonrpc_client_check_rpc_method(client, method_name);
380 	if (rc) {
381 		fprintf(stderr, "spdk_jsonrpc_client_check_rpc_method() failed: rc=%d errno=%d\n", rc, errno);
382 		goto out;
383 	}
384 
385 	rc = spdk_jsonrpc_client_check_null_params_method(client);
386 	if (rc) {
387 		fprintf(stderr, "spdk_jsonrpc_client_null_params_method() failed: rc=%d errno=%d\n", rc, errno);
388 		goto out;
389 	}
390 
391 	rc = spdk_jsonrpc_client_hook_conn_close(client);
392 	if (rc) {
393 		fprintf(stderr, "spdk_jsonrpc_client_hook_conn_close() failed: rc=%d errno=%d\n", rc, errno);
394 		goto out;
395 	}
396 
397 out:
398 	if (client) {
399 		spdk_jsonrpc_client_close(client);
400 	}
401 
402 	return (void *)(intptr_t)rc;
403 }
404 
405 int main(int argc, char **argv)
406 {
407 	pthread_t srv_tid, client_tid;
408 	int srv_tid_valid;
409 	int client_tid_valid = -1;
410 	intptr_t th_rc = INTPTR_MIN;
411 	int rc = 0, err_cnt = 0;
412 
413 	sem_init(&g_rpc_server_th_listening, 0, 0);
414 
415 	srv_tid_valid = pthread_create(&srv_tid, NULL, rpc_server_th, NULL);
416 	if (srv_tid_valid != 0) {
417 		fprintf(stderr, "pthread_create() failed to create server thread: %d\n", srv_tid_valid);
418 		goto out;
419 	}
420 
421 	client_tid_valid = pthread_create(&client_tid, NULL, rpc_client_th, NULL);
422 	if (client_tid_valid != 0) {
423 		fprintf(stderr, "pthread_create(): failed to create client thread: %d\n", client_tid_valid);
424 		goto out;
425 	}
426 
427 out:
428 	if (client_tid_valid == 0) {
429 		rc = pthread_join(client_tid, (void **)&th_rc);
430 		if (rc) {
431 			fprintf(stderr, "pthread_join() on client thread failed (rc: %d)\n", rc);
432 			err_cnt++;
433 		} else if (th_rc) {
434 			fprintf(stderr, "client thread failed reported failure(thread rc: %d)\n", (int)th_rc);
435 			err_cnt++;
436 		}
437 	}
438 
439 	g_rpc_server_th_stop = 1;
440 
441 	if (srv_tid_valid == 0) {
442 		rc = pthread_join(srv_tid, (void **)&th_rc);
443 		if (rc) {
444 			fprintf(stderr, "pthread_join() on server thread failed (rc: %d)\n", rc);
445 			err_cnt++;
446 		} else if (th_rc) {
447 			fprintf(stderr, "server thread failed reported failure(thread rc: %d)\n", (int)th_rc);
448 			err_cnt++;
449 		}
450 	}
451 
452 	if (g_conn_close_detected == false) {
453 		fprintf(stderr, "Connection close not detected\n");
454 		err_cnt++;
455 	}
456 
457 	sem_destroy(&g_rpc_server_th_listening);
458 
459 	fprintf(stderr, "%s\n", err_cnt == 0 ? "OK" : "FAILED");
460 	return err_cnt ? EXIT_FAILURE : 0;
461 }
462