xref: /spdk/test/rpc_client/rpc_client_test.c (revision 0098e636761237b77c12c30c2408263a5d2260cc)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (c) Intel Corporation.
3  *   All rights reserved.
4  */
5 
6 #include "spdk/stdinc.h"
7 #include "spdk/event.h"
8 #include "spdk/jsonrpc.h"
9 #include "spdk/util.h"
10 #include "spdk/rpc.h"
11 
12 
13 #define RPC_MAX_METHODS 200
14 #define JOIN_TIMEOUT_S 1
15 
16 static const char *g_rpcsock_addr = SPDK_DEFAULT_RPC_ADDR;
17 static int g_addr_family = AF_UNIX;
18 
19 #define RPC_MAX_METHODS 200
20 
21 struct get_jsonrpc_methods_resp {
22 	char *method_names[RPC_MAX_METHODS];
23 	size_t method_num;
24 };
25 
26 static int
27 _rpc_client_wait_for_response(struct spdk_jsonrpc_client *client)
28 {
29 	int rc;
30 
31 	do {
32 		rc = spdk_jsonrpc_client_poll(client, 1);
33 	} while (rc == 0 || rc == -ENOTCONN);
34 
35 	if (rc <= 0) {
36 		SPDK_ERRLOG("Failed to get response: %d\n", rc);
37 	}
38 
39 	return rc;
40 }
41 
42 static int
43 get_jsonrpc_method_json_parser(struct get_jsonrpc_methods_resp *resp,
44 			       const struct spdk_json_val *result)
45 {
46 	return spdk_json_decode_array(result, spdk_json_decode_string, resp->method_names,
47 				      RPC_MAX_METHODS, &resp->method_num, sizeof(char *));
48 }
49 
50 static int
51 spdk_jsonrpc_client_check_rpc_method(struct spdk_jsonrpc_client *client, char *method_name)
52 {
53 	int rc, i;
54 	struct spdk_jsonrpc_client_response *json_resp = NULL;
55 	struct get_jsonrpc_methods_resp resp = {};
56 	struct spdk_json_write_ctx *w;
57 	struct spdk_jsonrpc_client_request *request;
58 
59 	request = spdk_jsonrpc_client_create_request();
60 	if (request == NULL) {
61 		return -ENOMEM;
62 	}
63 
64 	w = spdk_jsonrpc_begin_request(request, 1, "rpc_get_methods");
65 	spdk_jsonrpc_end_request(request, w);
66 	spdk_jsonrpc_client_send_request(client, request);
67 
68 	rc = _rpc_client_wait_for_response(client);
69 	if (rc <= 0) {
70 		goto out;
71 	}
72 
73 	json_resp = spdk_jsonrpc_client_get_response(client);
74 	if (json_resp == NULL) {
75 		SPDK_ERRLOG("spdk_jsonrpc_client_get_response() failed\n");
76 		rc = -1;
77 		goto out;
78 
79 	}
80 
81 	/* Check for error response */
82 	if (json_resp->error != NULL) {
83 		SPDK_ERRLOG("Unexpected error response\n");
84 		rc = -1;
85 		goto out;
86 	}
87 
88 	assert(json_resp->result);
89 
90 	rc = get_jsonrpc_method_json_parser(&resp, json_resp->result);
91 	if (rc) {
92 		SPDK_ERRLOG("get_jsonrpc_method_json_parser() failed\n");
93 		goto out;
94 	}
95 
96 	for (i = 0; i < (int)resp.method_num; i++) {
97 		if (strcmp(method_name, resp.method_names[i]) == 0) {
98 			rc = 0;
99 			goto out;
100 		}
101 	}
102 
103 	rc = -1;
104 	SPDK_ERRLOG("Method '%s' not found in response\n", method_name);
105 
106 out:
107 	for (i = 0; i < (int)resp.method_num; i++) {
108 		SPDK_NOTICELOG("%s\n", resp.method_names[i]);
109 		free(resp.method_names[i]);
110 	}
111 
112 	spdk_jsonrpc_client_free_response(json_resp);
113 	return rc;
114 }
115 
116 static int
117 spdk_jsonrpc_client_check_null_params_method(struct spdk_jsonrpc_client *client)
118 {
119 	int rc;
120 	bool res = false;
121 	struct spdk_jsonrpc_client_response *json_resp = NULL;
122 	struct spdk_json_write_ctx *w;
123 	struct spdk_jsonrpc_client_request *request;
124 
125 	request = spdk_jsonrpc_client_create_request();
126 	if (request == NULL) {
127 		return -ENOMEM;
128 	}
129 
130 	w = spdk_jsonrpc_begin_request(request, 1, "test_null_params");
131 	spdk_json_write_name(w, "params");
132 	spdk_json_write_null(w);
133 	spdk_jsonrpc_end_request(request, w);
134 	spdk_jsonrpc_client_send_request(client, request);
135 
136 	rc = _rpc_client_wait_for_response(client);
137 	if (rc <= 0) {
138 		goto out;
139 	}
140 
141 	json_resp = spdk_jsonrpc_client_get_response(client);
142 	if (json_resp == NULL) {
143 		SPDK_ERRLOG("spdk_jsonrpc_client_get_response() failed\n");
144 		rc = -1;
145 		goto out;
146 
147 	}
148 
149 	/* Check for error response */
150 	if (json_resp->error != NULL) {
151 		SPDK_ERRLOG("Unexpected error response\n");
152 		rc = -1;
153 		goto out;
154 	}
155 
156 	assert(json_resp->result);
157 
158 	if (spdk_json_decode_bool(json_resp->result, &res) != 0 || res != true) {
159 		SPDK_ERRLOG("Response is not a boolean or it is not 'true'\n");
160 		rc = -EINVAL;
161 		goto out;
162 	} else {
163 		rc = 0;
164 	}
165 
166 out:
167 	spdk_jsonrpc_client_free_response(json_resp);
168 	return rc;
169 }
170 
171 static void
172 rpc_test_method_startup(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
173 {
174 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
175 					 "rpc_test_method_startup(): Method body not implemented");
176 }
177 SPDK_RPC_REGISTER("test_method_startup", rpc_test_method_startup, SPDK_RPC_STARTUP)
178 
179 static void
180 rpc_test_method_runtime(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
181 {
182 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
183 					 "rpc_test_method_runtime(): Method body not implemented");
184 }
185 SPDK_RPC_REGISTER("test_method_runtime", rpc_test_method_runtime, SPDK_RPC_RUNTIME)
186 
187 static void
188 rpc_test_method_null_params(struct spdk_jsonrpc_request *request,
189 			    const struct spdk_json_val *params)
190 {
191 	if (params != NULL) {
192 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
193 						 "rpc_test_method_null_params(): Parameters are not NULL");
194 		return;
195 	}
196 
197 	spdk_jsonrpc_send_bool_response(request, true);
198 }
199 SPDK_RPC_REGISTER("test_null_params", rpc_test_method_null_params, SPDK_RPC_RUNTIME)
200 
201 static bool g_conn_close_detected;
202 
203 static void
204 rpc_test_conn_close_cb(struct spdk_jsonrpc_server_conn *conn, void *ctx)
205 {
206 	assert((intptr_t)ctx == 42);
207 	g_conn_close_detected = true;
208 }
209 
210 static void
211 rpc_hook_conn_close(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
212 {
213 	struct spdk_jsonrpc_server_conn *conn = spdk_jsonrpc_get_conn(request);
214 	int rc;
215 
216 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
217 	if (rc != 0) {
218 
219 		rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
220 		assert(rc == -ENOSPC);
221 	}
222 
223 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
224 	if (rc != -EEXIST) {
225 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
226 						 "rpc_test_method_conn_close_detect(): rc != -EEXIST");
227 		return;
228 	}
229 
230 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(43));
231 	if (rc != -ENOSPC) {
232 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
233 						 "rpc_test_method_conn_close_detect(): rc != -ENOSPC");
234 		return;
235 	}
236 
237 	spdk_jsonrpc_send_bool_response(request, true);
238 }
239 SPDK_RPC_REGISTER("hook_conn_close", rpc_hook_conn_close, SPDK_RPC_RUNTIME | SPDK_RPC_STARTUP)
240 
241 static int
242 spdk_jsonrpc_client_hook_conn_close(struct spdk_jsonrpc_client *client)
243 {
244 	int rc;
245 	bool res = false;
246 	struct spdk_jsonrpc_client_response *json_resp = NULL;
247 	struct spdk_json_write_ctx *w;
248 	struct spdk_jsonrpc_client_request *request;
249 
250 	request = spdk_jsonrpc_client_create_request();
251 	if (request == NULL) {
252 		return -ENOMEM;
253 	}
254 
255 	w = spdk_jsonrpc_begin_request(request, 1, "hook_conn_close");
256 	spdk_jsonrpc_end_request(request, w);
257 	spdk_jsonrpc_client_send_request(client, request);
258 
259 	rc = _rpc_client_wait_for_response(client);
260 	if (rc <= 0) {
261 		goto out;
262 	}
263 
264 	json_resp = spdk_jsonrpc_client_get_response(client);
265 	if (json_resp == NULL) {
266 		SPDK_ERRLOG("spdk_jsonrpc_client_get_response() failed\n");
267 		rc = -errno;
268 		goto out;
269 
270 	}
271 
272 	/* Check for error response */
273 	if (json_resp->error != NULL) {
274 		SPDK_ERRLOG("Unexpected error response: %.*s\n", json_resp->error->len,
275 			    (char *)json_resp->error->start);
276 		rc = -EIO;
277 		goto out;
278 	}
279 
280 	assert(json_resp->result);
281 	if (spdk_json_decode_bool(json_resp->result, &res) != 0 || res != true) {
282 		SPDK_ERRLOG("Response is not and boolean or if not 'true'\n");
283 		rc = -EINVAL;
284 		goto out;
285 	}
286 
287 	rc = 0;
288 out:
289 	spdk_jsonrpc_client_free_response(json_resp);
290 	return rc;
291 }
292 
293 volatile int g_rpc_server_th_stop;
294 static sem_t g_rpc_server_th_listening;
295 
296 static void *
297 rpc_server_th(void *arg)
298 {
299 	int rc;
300 
301 	rc = spdk_rpc_listen(g_rpcsock_addr);
302 	if (rc) {
303 		fprintf(stderr, "spdk_rpc_listen() failed: %d\n", rc);
304 		sem_post(&g_rpc_server_th_listening);
305 		goto out;
306 	}
307 
308 	sem_post(&g_rpc_server_th_listening);
309 
310 	while (!g_rpc_server_th_stop) {
311 		spdk_rpc_accept();
312 		usleep(50);
313 	}
314 
315 	spdk_rpc_close();
316 out:
317 	return (void *)(intptr_t)rc;
318 }
319 
320 static void *
321 rpc_client_th(void *arg)
322 {
323 	struct spdk_jsonrpc_client *client = NULL;
324 	char *method_name = "rpc_get_methods";
325 	int rc;
326 
327 
328 	rc = sem_wait(&g_rpc_server_th_listening);
329 	if (rc == -1) {
330 		fprintf(stderr, "Timeout waiting for server thread to start listening: rc=%d errno=%d\n", rc,
331 			errno);
332 		goto out;
333 	}
334 
335 	client = spdk_jsonrpc_client_connect(g_rpcsock_addr, g_addr_family);
336 	if (!client) {
337 		fprintf(stderr, "spdk_jsonrpc_client_connect() failed: %d\n", errno);
338 		rc = -1;
339 		goto out;
340 	}
341 
342 	rc = spdk_jsonrpc_client_check_rpc_method(client, method_name);
343 	if (rc) {
344 		fprintf(stderr, "spdk_jsonrpc_client_check_rpc_method() failed: rc=%d errno=%d\n", rc, errno);
345 		goto out;
346 	}
347 
348 	rc = spdk_jsonrpc_client_check_null_params_method(client);
349 	if (rc) {
350 		fprintf(stderr, "spdk_jsonrpc_client_null_params_method() failed: rc=%d errno=%d\n", rc, errno);
351 		goto out;
352 	}
353 
354 	rc = spdk_jsonrpc_client_hook_conn_close(client);
355 	if (rc) {
356 		fprintf(stderr, "spdk_jsonrpc_client_hook_conn_close() failed: rc=%d errno=%d\n", rc, errno);
357 		goto out;
358 	}
359 
360 out:
361 	if (client) {
362 		spdk_jsonrpc_client_close(client);
363 	}
364 
365 	return (void *)(intptr_t)rc;
366 }
367 
368 int
369 main(int argc, char **argv)
370 {
371 	pthread_t srv_tid, client_tid;
372 	int srv_tid_valid;
373 	int client_tid_valid = -1;
374 	intptr_t th_rc = INTPTR_MIN;
375 	int rc = 0, err_cnt = 0;
376 
377 	sem_init(&g_rpc_server_th_listening, 0, 0);
378 
379 	srv_tid_valid = pthread_create(&srv_tid, NULL, rpc_server_th, NULL);
380 	if (srv_tid_valid != 0) {
381 		fprintf(stderr, "pthread_create() failed to create server thread: %d\n", srv_tid_valid);
382 		goto out;
383 	}
384 
385 	client_tid_valid = pthread_create(&client_tid, NULL, rpc_client_th, NULL);
386 	if (client_tid_valid != 0) {
387 		fprintf(stderr, "pthread_create(): failed to create client thread: %d\n", client_tid_valid);
388 		goto out;
389 	}
390 
391 out:
392 	if (client_tid_valid == 0) {
393 		rc = pthread_join(client_tid, (void **)&th_rc);
394 		if (rc) {
395 			fprintf(stderr, "pthread_join() on client thread failed (rc: %d)\n", rc);
396 			err_cnt++;
397 		} else if (th_rc) {
398 			fprintf(stderr, "client thread failed reported failure(thread rc: %d)\n", (int)th_rc);
399 			err_cnt++;
400 		}
401 	}
402 
403 	g_rpc_server_th_stop = 1;
404 
405 	if (srv_tid_valid == 0) {
406 		rc = pthread_join(srv_tid, (void **)&th_rc);
407 		if (rc) {
408 			fprintf(stderr, "pthread_join() on server thread failed (rc: %d)\n", rc);
409 			err_cnt++;
410 		} else if (th_rc) {
411 			fprintf(stderr, "server thread failed reported failure(thread rc: %d)\n", (int)th_rc);
412 			err_cnt++;
413 		}
414 	}
415 
416 	if (g_conn_close_detected == false) {
417 		fprintf(stderr, "Connection close not detected\n");
418 		err_cnt++;
419 	}
420 
421 	sem_destroy(&g_rpc_server_th_listening);
422 
423 	fprintf(stderr, "%s\n", err_cnt == 0 ? "OK" : "FAILED");
424 	return err_cnt ? EXIT_FAILURE : 0;
425 }
426