xref: /spdk/test/rpc_client/rpc_client_test.c (revision 552e21cce6cccbf833ed9109827e08337377d7ce)
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, "get_rpc_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 void
145 rpc_test_method_startup(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
146 {
147 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
148 					 "rpc_test_method_startup(): Method body not implemented");
149 }
150 SPDK_RPC_REGISTER("test_method_startup", rpc_test_method_startup, SPDK_RPC_STARTUP)
151 
152 static void
153 rpc_test_method_runtime(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
154 {
155 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
156 					 "rpc_test_method_runtime(): Method body not implemented");
157 }
158 SPDK_RPC_REGISTER("test_method_runtime", rpc_test_method_runtime, SPDK_RPC_RUNTIME)
159 
160 static bool g_conn_close_detected;
161 
162 static void
163 rpc_test_conn_close_cb(struct spdk_jsonrpc_server_conn *conn, void *ctx)
164 {
165 	assert((intptr_t)ctx == 42);
166 	g_conn_close_detected = true;
167 }
168 
169 static void
170 rpc_hook_conn_close(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
171 {
172 	struct spdk_jsonrpc_server_conn *conn = spdk_jsonrpc_get_conn(request);
173 	struct spdk_json_write_ctx *w;
174 	int rc;
175 
176 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
177 	if (rc != 0) {
178 
179 		rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
180 		assert(rc == -ENOSPC);
181 	}
182 
183 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(42));
184 	if (rc != -EEXIST) {
185 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
186 						 "rpc_test_method_conn_close_detect(): rc != -EEXIST");
187 		return;
188 	}
189 
190 	rc = spdk_jsonrpc_conn_add_close_cb(conn, rpc_test_conn_close_cb, (void *)(intptr_t)(43));
191 	if (rc != -ENOSPC) {
192 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
193 						 "rpc_test_method_conn_close_detect(): rc != -ENOSPC");
194 		return;
195 	}
196 
197 	w = spdk_jsonrpc_begin_result(request);
198 	assert(w != NULL);
199 	spdk_json_write_bool(w, true);
200 	spdk_jsonrpc_end_result(request, w);
201 
202 }
203 SPDK_RPC_REGISTER("hook_conn_close", rpc_hook_conn_close, SPDK_RPC_RUNTIME | SPDK_RPC_STARTUP)
204 
205 static int
206 spdk_jsonrpc_client_hook_conn_close(struct spdk_jsonrpc_client *client)
207 {
208 	int rc;
209 	bool res = false;
210 	struct spdk_jsonrpc_client_response *json_resp = NULL;
211 	struct spdk_json_write_ctx *w;
212 	struct spdk_jsonrpc_client_request *request;
213 
214 	request = spdk_jsonrpc_client_create_request();
215 	if (request == NULL) {
216 		return -ENOMEM;
217 	}
218 
219 	w = spdk_jsonrpc_begin_request(request, 1, "hook_conn_close");
220 	spdk_jsonrpc_end_request(request, w);
221 	spdk_jsonrpc_client_send_request(client, request);
222 
223 	rc = _rpc_client_wait_for_response(client);
224 	if (rc <= 0) {
225 		goto out;
226 	}
227 
228 	json_resp = spdk_jsonrpc_client_get_response(client);
229 	if (json_resp == NULL) {
230 		SPDK_ERRLOG("spdk_jsonrpc_client_get_response() failed\n");
231 		rc = -errno;
232 		goto out;
233 
234 	}
235 
236 	/* Check for error response */
237 	if (json_resp->error != NULL) {
238 		SPDK_ERRLOG("Unexpected error response: %*s\n", json_resp->error->len,
239 			    (char *)json_resp->error->start);
240 		rc = -EIO;
241 		goto out;
242 	}
243 
244 	assert(json_resp->result);
245 	if (spdk_json_decode_bool(json_resp->result, &res) != 0 || res != true) {
246 		SPDK_ERRLOG("Response is not and boolean or if not 'true'\n");
247 		rc = -EINVAL;
248 		goto out;
249 	}
250 
251 	rc = 0;
252 out:
253 	spdk_jsonrpc_client_free_response(json_resp);
254 	return rc;
255 }
256 
257 /* Helper function */
258 static int
259 _sem_timedwait(sem_t *sem, __time_t sec)
260 {
261 	struct timespec timeout;
262 
263 	clock_gettime(CLOCK_REALTIME, &timeout);
264 	timeout.tv_sec += sec;
265 
266 	return sem_timedwait(sem, &timeout);
267 }
268 
269 volatile int g_rpc_server_th_stop;
270 static sem_t g_rpc_server_th_listening;
271 static sem_t g_rpc_server_th_done;
272 
273 static void *
274 rpc_server_th(void *arg)
275 {
276 	int rc;
277 
278 	rc = spdk_rpc_listen(g_rpcsock_addr);
279 	if (rc) {
280 		fprintf(stderr, "spdk_rpc_listen() failed: %d\n", rc);
281 		goto out;
282 	}
283 
284 	sem_post(&g_rpc_server_th_listening);
285 
286 	while (!g_rpc_server_th_stop) {
287 		spdk_rpc_accept();
288 		usleep(50);
289 	}
290 
291 	spdk_rpc_close();
292 out:
293 	sem_post(&g_rpc_server_th_done);
294 
295 	return (void *)(intptr_t)rc;
296 }
297 
298 static sem_t g_rpc_client_th_done;
299 
300 static void *
301 rpc_client_th(void *arg)
302 {
303 	struct spdk_jsonrpc_client *client = NULL;
304 	char *method_name = "get_rpc_methods";
305 	int rc;
306 
307 
308 	rc = _sem_timedwait(&g_rpc_server_th_listening, 2);
309 	if (rc == -1) {
310 		fprintf(stderr, "Timeout waiting for server thread to start listening: rc=%d errno=%d\n", rc,
311 			errno);
312 		goto out;
313 	}
314 
315 	client = spdk_jsonrpc_client_connect(g_rpcsock_addr, g_addr_family);
316 	if (!client) {
317 		fprintf(stderr, "spdk_jsonrpc_client_connect() failed: %d\n", errno);
318 		rc = -1;
319 		goto out;
320 	}
321 
322 	rc = spdk_jsonrpc_client_check_rpc_method(client, method_name);
323 	if (rc) {
324 		fprintf(stderr, "spdk_jsonrpc_client_check_rpc_method() failed: rc=%d errno=%d\n", rc, errno);
325 		goto out;
326 	}
327 
328 	rc = spdk_jsonrpc_client_hook_conn_close(client);
329 	if (rc) {
330 		fprintf(stderr, "spdk_jsonrpc_client_hook_conn_close() failed: rc=%d errno=%d\n", rc, errno);
331 		goto out;
332 	}
333 
334 out:
335 	if (client) {
336 		spdk_jsonrpc_client_close(client);
337 	}
338 
339 	sem_post(&g_rpc_client_th_done);
340 	return (void *)(intptr_t)rc;
341 }
342 
343 int main(int argc, char **argv)
344 {
345 	pthread_t srv_tid, client_tid;
346 	int srv_tid_valid;
347 	int client_tid_valid = -1;
348 	intptr_t th_rc = INTPTR_MIN;
349 	int rc = 0, err_cnt = 0;
350 
351 	sem_init(&g_rpc_server_th_listening, 0, 0);
352 	sem_init(&g_rpc_server_th_done, 0, 0);
353 	sem_init(&g_rpc_client_th_done, 0, 0);
354 
355 	srv_tid_valid = pthread_create(&srv_tid, NULL, rpc_server_th, NULL);
356 	if (srv_tid_valid != 0) {
357 		fprintf(stderr, "pthread_create() failed to create server thread: %d\n", srv_tid_valid);
358 		goto out;
359 	}
360 
361 	client_tid_valid = pthread_create(&client_tid, NULL, rpc_client_th, NULL);
362 	if (client_tid_valid != 0) {
363 		fprintf(stderr, "pthread_create(): failed to create client thread: %d\n", client_tid_valid);
364 		goto out;
365 	}
366 
367 out:
368 	if (client_tid_valid == 0) {
369 		rc = _sem_timedwait(&g_rpc_client_th_done, JOIN_TIMEOUT_S);
370 		if (rc) {
371 			fprintf(stderr, "failed to join client thread (rc: %d)\n", rc);
372 			err_cnt++;
373 		}
374 
375 		rc = pthread_join(client_tid, (void **)&th_rc);
376 		if (rc) {
377 			fprintf(stderr, "pthread_join() on cliennt thread failed (rc: %d)\n", rc);
378 			err_cnt++;
379 		} else if (th_rc) {
380 			fprintf(stderr, "cliennt thread failed reported failure(thread rc: %d)\n", (int)th_rc);
381 			err_cnt++;
382 		}
383 	}
384 
385 	g_rpc_server_th_stop = 1;
386 
387 	if (srv_tid_valid == 0) {
388 		rc = _sem_timedwait(&g_rpc_server_th_done, JOIN_TIMEOUT_S);
389 		if (rc) {
390 			fprintf(stderr, "server thread failed to exit in %d sec: (rc: %d)\n", JOIN_TIMEOUT_S, rc);
391 			err_cnt++;
392 		}
393 
394 		rc = pthread_join(srv_tid, (void **)&th_rc);
395 		if (rc) {
396 			fprintf(stderr, "pthread_join() on cliennt thread failed (rc: %d)\n", rc);
397 			err_cnt++;
398 		} else if (th_rc) {
399 			fprintf(stderr, "cliennt thread failed reported failure(thread rc: %d)\n", (int)th_rc);
400 			err_cnt++;
401 		}
402 	}
403 
404 	if (g_conn_close_detected == false) {
405 		fprintf(stderr, "Connection close not detected\n");
406 		err_cnt++;
407 	}
408 
409 	sem_destroy(&g_rpc_server_th_listening);
410 	sem_destroy(&g_rpc_server_th_done);
411 	sem_destroy(&g_rpc_client_th_done);
412 
413 	fprintf(stderr, "%s\n", err_cnt == 0 ? "OK" : "FAILED");
414 	return err_cnt ? EXIT_FAILURE : 0;
415 }
416