xref: /spdk/test/rpc_client/rpc_client_test.c (revision ae7b5890ef728af40bd233a5011b924c482603bf)
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 /* Helper function */
331 static int
332 _sem_timedwait(sem_t *sem, __time_t sec)
333 {
334 	struct timespec timeout;
335 
336 	clock_gettime(CLOCK_REALTIME, &timeout);
337 	timeout.tv_sec += sec;
338 
339 	return sem_timedwait(sem, &timeout);
340 }
341 
342 volatile int g_rpc_server_th_stop;
343 static sem_t g_rpc_server_th_listening;
344 static sem_t g_rpc_server_th_done;
345 
346 static void *
347 rpc_server_th(void *arg)
348 {
349 	int rc;
350 
351 	rc = spdk_rpc_listen(g_rpcsock_addr);
352 	if (rc) {
353 		fprintf(stderr, "spdk_rpc_listen() failed: %d\n", rc);
354 		goto out;
355 	}
356 
357 	sem_post(&g_rpc_server_th_listening);
358 
359 	while (!g_rpc_server_th_stop) {
360 		spdk_rpc_accept();
361 		usleep(50);
362 	}
363 
364 	spdk_rpc_close();
365 out:
366 	sem_post(&g_rpc_server_th_done);
367 
368 	return (void *)(intptr_t)rc;
369 }
370 
371 static sem_t g_rpc_client_th_done;
372 
373 static void *
374 rpc_client_th(void *arg)
375 {
376 	struct spdk_jsonrpc_client *client = NULL;
377 	char *method_name = "rpc_get_methods";
378 	int rc;
379 
380 
381 	rc = _sem_timedwait(&g_rpc_server_th_listening, 2);
382 	if (rc == -1) {
383 		fprintf(stderr, "Timeout waiting for server thread to start listening: rc=%d errno=%d\n", rc,
384 			errno);
385 		goto out;
386 	}
387 
388 	client = spdk_jsonrpc_client_connect(g_rpcsock_addr, g_addr_family);
389 	if (!client) {
390 		fprintf(stderr, "spdk_jsonrpc_client_connect() failed: %d\n", errno);
391 		rc = -1;
392 		goto out;
393 	}
394 
395 	rc = spdk_jsonrpc_client_check_rpc_method(client, method_name);
396 	if (rc) {
397 		fprintf(stderr, "spdk_jsonrpc_client_check_rpc_method() failed: rc=%d errno=%d\n", rc, errno);
398 		goto out;
399 	}
400 
401 	rc = spdk_jsonrpc_client_check_null_params_method(client);
402 	if (rc) {
403 		fprintf(stderr, "spdk_jsonrpc_client_null_params_method() failed: rc=%d errno=%d\n", rc, errno);
404 		goto out;
405 	}
406 
407 	rc = spdk_jsonrpc_client_hook_conn_close(client);
408 	if (rc) {
409 		fprintf(stderr, "spdk_jsonrpc_client_hook_conn_close() failed: rc=%d errno=%d\n", rc, errno);
410 		goto out;
411 	}
412 
413 out:
414 	if (client) {
415 		spdk_jsonrpc_client_close(client);
416 	}
417 
418 	sem_post(&g_rpc_client_th_done);
419 	return (void *)(intptr_t)rc;
420 }
421 
422 int main(int argc, char **argv)
423 {
424 	pthread_t srv_tid, client_tid;
425 	int srv_tid_valid;
426 	int client_tid_valid = -1;
427 	intptr_t th_rc = INTPTR_MIN;
428 	int rc = 0, err_cnt = 0;
429 
430 	sem_init(&g_rpc_server_th_listening, 0, 0);
431 	sem_init(&g_rpc_server_th_done, 0, 0);
432 	sem_init(&g_rpc_client_th_done, 0, 0);
433 
434 	srv_tid_valid = pthread_create(&srv_tid, NULL, rpc_server_th, NULL);
435 	if (srv_tid_valid != 0) {
436 		fprintf(stderr, "pthread_create() failed to create server thread: %d\n", srv_tid_valid);
437 		goto out;
438 	}
439 
440 	client_tid_valid = pthread_create(&client_tid, NULL, rpc_client_th, NULL);
441 	if (client_tid_valid != 0) {
442 		fprintf(stderr, "pthread_create(): failed to create client thread: %d\n", client_tid_valid);
443 		goto out;
444 	}
445 
446 out:
447 	if (client_tid_valid == 0) {
448 		rc = _sem_timedwait(&g_rpc_client_th_done, JOIN_TIMEOUT_S);
449 		if (rc) {
450 			fprintf(stderr, "failed to join client thread (rc: %d)\n", rc);
451 			err_cnt++;
452 		}
453 
454 		rc = pthread_join(client_tid, (void **)&th_rc);
455 		if (rc) {
456 			fprintf(stderr, "pthread_join() on cliennt thread failed (rc: %d)\n", rc);
457 			err_cnt++;
458 		} else if (th_rc) {
459 			fprintf(stderr, "cliennt thread failed reported failure(thread rc: %d)\n", (int)th_rc);
460 			err_cnt++;
461 		}
462 	}
463 
464 	g_rpc_server_th_stop = 1;
465 
466 	if (srv_tid_valid == 0) {
467 		rc = _sem_timedwait(&g_rpc_server_th_done, JOIN_TIMEOUT_S);
468 		if (rc) {
469 			fprintf(stderr, "server thread failed to exit in %d sec: (rc: %d)\n", JOIN_TIMEOUT_S, rc);
470 			err_cnt++;
471 		}
472 
473 		rc = pthread_join(srv_tid, (void **)&th_rc);
474 		if (rc) {
475 			fprintf(stderr, "pthread_join() on cliennt thread failed (rc: %d)\n", rc);
476 			err_cnt++;
477 		} else if (th_rc) {
478 			fprintf(stderr, "cliennt thread failed reported failure(thread rc: %d)\n", (int)th_rc);
479 			err_cnt++;
480 		}
481 	}
482 
483 	if (g_conn_close_detected == false) {
484 		fprintf(stderr, "Connection close not detected\n");
485 		err_cnt++;
486 	}
487 
488 	sem_destroy(&g_rpc_server_th_listening);
489 	sem_destroy(&g_rpc_server_th_done);
490 	sem_destroy(&g_rpc_client_th_done);
491 
492 	fprintf(stderr, "%s\n", err_cnt == 0 ? "OK" : "FAILED");
493 	return err_cnt ? EXIT_FAILURE : 0;
494 }
495