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