xref: /spdk/test/rpc_client/rpc_client_test.c (revision 7506a7aa53d239f533af3bc768f0d2af55e735fe)
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 	if (params != NULL) {
220 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
221 						 "rpc_test_method_null_params(): Parameters are not NULL");
222 		return;
223 	}
224 
225 	spdk_jsonrpc_send_bool_response(request, true);
226 }
227 SPDK_RPC_REGISTER("test_null_params", rpc_test_method_null_params, SPDK_RPC_RUNTIME)
228 
229 static bool g_conn_close_detected;
230 
231 static void
232 rpc_test_conn_close_cb(struct spdk_jsonrpc_server_conn *conn, void *ctx)
233 {
234 	assert((intptr_t)ctx == 42);
235 	g_conn_close_detected = true;
236 }
237 
238 static void
239 rpc_hook_conn_close(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
240 {
241 	struct spdk_jsonrpc_server_conn *conn = spdk_jsonrpc_get_conn(request);
242 	int rc;
243 
244 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
245 	if (rc != 0) {
246 
247 		rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
248 		assert(rc == -ENOSPC);
249 	}
250 
251 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
252 	if (rc != -EEXIST) {
253 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
254 						 "rpc_test_method_conn_close_detect(): rc != -EEXIST");
255 		return;
256 	}
257 
258 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(43));
259 	if (rc != -ENOSPC) {
260 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
261 						 "rpc_test_method_conn_close_detect(): rc != -ENOSPC");
262 		return;
263 	}
264 
265 	spdk_jsonrpc_send_bool_response(request, true);
266 }
267 SPDK_RPC_REGISTER("hook_conn_close", rpc_hook_conn_close, SPDK_RPC_RUNTIME | SPDK_RPC_STARTUP)
268 
269 static int
270 spdk_jsonrpc_client_hook_conn_close(struct spdk_jsonrpc_client *client)
271 {
272 	int rc;
273 	bool res = false;
274 	struct spdk_jsonrpc_client_response *json_resp = NULL;
275 	struct spdk_json_write_ctx *w;
276 	struct spdk_jsonrpc_client_request *request;
277 
278 	request = spdk_jsonrpc_client_create_request();
279 	if (request == NULL) {
280 		return -ENOMEM;
281 	}
282 
283 	w = spdk_jsonrpc_begin_request(request, 1, "hook_conn_close");
284 	spdk_jsonrpc_end_request(request, w);
285 	spdk_jsonrpc_client_send_request(client, request);
286 
287 	rc = _rpc_client_wait_for_response(client);
288 	if (rc <= 0) {
289 		goto out;
290 	}
291 
292 	json_resp = spdk_jsonrpc_client_get_response(client);
293 	if (json_resp == NULL) {
294 		SPDK_ERRLOG("spdk_jsonrpc_client_get_response() failed\n");
295 		rc = -errno;
296 		goto out;
297 
298 	}
299 
300 	/* Check for error response */
301 	if (json_resp->error != NULL) {
302 		SPDK_ERRLOG("Unexpected error response: %.*s\n", json_resp->error->len,
303 			    (char *)json_resp->error->start);
304 		rc = -EIO;
305 		goto out;
306 	}
307 
308 	assert(json_resp->result);
309 	if (spdk_json_decode_bool(json_resp->result, &res) != 0 || res != true) {
310 		SPDK_ERRLOG("Response is not and boolean or if not 'true'\n");
311 		rc = -EINVAL;
312 		goto out;
313 	}
314 
315 	rc = 0;
316 out:
317 	spdk_jsonrpc_client_free_response(json_resp);
318 	return rc;
319 }
320 
321 volatile int g_rpc_server_th_stop;
322 static sem_t g_rpc_server_th_listening;
323 
324 static void *
325 rpc_server_th(void *arg)
326 {
327 	int rc;
328 
329 	rc = spdk_rpc_listen(g_rpcsock_addr);
330 	if (rc) {
331 		fprintf(stderr, "spdk_rpc_listen() failed: %d\n", rc);
332 		sem_post(&g_rpc_server_th_listening);
333 		goto out;
334 	}
335 
336 	sem_post(&g_rpc_server_th_listening);
337 
338 	while (!g_rpc_server_th_stop) {
339 		spdk_rpc_accept();
340 		usleep(50);
341 	}
342 
343 	spdk_rpc_close();
344 out:
345 	return (void *)(intptr_t)rc;
346 }
347 
348 static void *
349 rpc_client_th(void *arg)
350 {
351 	struct spdk_jsonrpc_client *client = NULL;
352 	char *method_name = "rpc_get_methods";
353 	int rc;
354 
355 
356 	rc = sem_wait(&g_rpc_server_th_listening);
357 	if (rc == -1) {
358 		fprintf(stderr, "Timeout waiting for server thread to start listening: rc=%d errno=%d\n", rc,
359 			errno);
360 		goto out;
361 	}
362 
363 	client = spdk_jsonrpc_client_connect(g_rpcsock_addr, g_addr_family);
364 	if (!client) {
365 		fprintf(stderr, "spdk_jsonrpc_client_connect() failed: %d\n", errno);
366 		rc = -1;
367 		goto out;
368 	}
369 
370 	rc = spdk_jsonrpc_client_check_rpc_method(client, method_name);
371 	if (rc) {
372 		fprintf(stderr, "spdk_jsonrpc_client_check_rpc_method() failed: rc=%d errno=%d\n", rc, errno);
373 		goto out;
374 	}
375 
376 	rc = spdk_jsonrpc_client_check_null_params_method(client);
377 	if (rc) {
378 		fprintf(stderr, "spdk_jsonrpc_client_null_params_method() failed: rc=%d errno=%d\n", rc, errno);
379 		goto out;
380 	}
381 
382 	rc = spdk_jsonrpc_client_hook_conn_close(client);
383 	if (rc) {
384 		fprintf(stderr, "spdk_jsonrpc_client_hook_conn_close() failed: rc=%d errno=%d\n", rc, errno);
385 		goto out;
386 	}
387 
388 out:
389 	if (client) {
390 		spdk_jsonrpc_client_close(client);
391 	}
392 
393 	return (void *)(intptr_t)rc;
394 }
395 
396 int main(int argc, char **argv)
397 {
398 	pthread_t srv_tid, client_tid;
399 	int srv_tid_valid;
400 	int client_tid_valid = -1;
401 	intptr_t th_rc = INTPTR_MIN;
402 	int rc = 0, err_cnt = 0;
403 
404 	sem_init(&g_rpc_server_th_listening, 0, 0);
405 
406 	srv_tid_valid = pthread_create(&srv_tid, NULL, rpc_server_th, NULL);
407 	if (srv_tid_valid != 0) {
408 		fprintf(stderr, "pthread_create() failed to create server thread: %d\n", srv_tid_valid);
409 		goto out;
410 	}
411 
412 	client_tid_valid = pthread_create(&client_tid, NULL, rpc_client_th, NULL);
413 	if (client_tid_valid != 0) {
414 		fprintf(stderr, "pthread_create(): failed to create client thread: %d\n", client_tid_valid);
415 		goto out;
416 	}
417 
418 out:
419 	if (client_tid_valid == 0) {
420 		rc = pthread_join(client_tid, (void **)&th_rc);
421 		if (rc) {
422 			fprintf(stderr, "pthread_join() on client thread failed (rc: %d)\n", rc);
423 			err_cnt++;
424 		} else if (th_rc) {
425 			fprintf(stderr, "client thread failed reported failure(thread rc: %d)\n", (int)th_rc);
426 			err_cnt++;
427 		}
428 	}
429 
430 	g_rpc_server_th_stop = 1;
431 
432 	if (srv_tid_valid == 0) {
433 		rc = pthread_join(srv_tid, (void **)&th_rc);
434 		if (rc) {
435 			fprintf(stderr, "pthread_join() on server thread failed (rc: %d)\n", rc);
436 			err_cnt++;
437 		} else if (th_rc) {
438 			fprintf(stderr, "server thread failed reported failure(thread rc: %d)\n", (int)th_rc);
439 			err_cnt++;
440 		}
441 	}
442 
443 	if (g_conn_close_detected == false) {
444 		fprintf(stderr, "Connection close not detected\n");
445 		err_cnt++;
446 	}
447 
448 	sem_destroy(&g_rpc_server_th_listening);
449 
450 	fprintf(stderr, "%s\n", err_cnt == 0 ? "OK" : "FAILED");
451 	return err_cnt ? EXIT_FAILURE : 0;
452 }
453