xref: /spdk/test/rpc_client/rpc_client_test.c (revision 7d38f16674f1bc7071d08f4d4dad68fc3ffad965)
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 get_jsonrpc_method_json_parser(struct get_jsonrpc_methods_resp *resp,
56 			       const struct spdk_json_val *result)
57 {
58 	return spdk_json_decode_array(result, spdk_json_decode_string, resp->method_names,
59 				      RPC_MAX_METHODS, &resp->method_num, sizeof(char *));
60 }
61 
62 static int
63 spdk_jsonrpc_client_check_rpc_method(struct spdk_jsonrpc_client *client, char *method_name)
64 {
65 	int rc, i;
66 	struct spdk_jsonrpc_client_response *json_resp = NULL;
67 	struct get_jsonrpc_methods_resp resp = {};
68 	struct spdk_json_write_ctx *w;
69 	struct spdk_jsonrpc_client_request *request;
70 
71 	request = spdk_jsonrpc_client_create_request();
72 	if (request == NULL) {
73 		return -ENOMEM;
74 	}
75 
76 	w = spdk_jsonrpc_begin_request(request, 1, "get_rpc_methods");
77 	spdk_jsonrpc_end_request(request, w);
78 	spdk_jsonrpc_client_send_request(client, request);
79 
80 	do {
81 		rc = spdk_jsonrpc_client_poll(client, 1);
82 	} while (rc == 0 || rc == -ENOTCONN);
83 
84 	if (rc <= 0) {
85 		SPDK_ERRLOG("Failed to get response: %d\n", rc);
86 		rc = -1;
87 		goto out;
88 	}
89 
90 	json_resp = spdk_jsonrpc_client_get_response(client);
91 	if (json_resp == NULL) {
92 		SPDK_ERRLOG("spdk_jsonrpc_client_get_response() failed\n");
93 		rc = -1;
94 		goto out;
95 
96 	}
97 
98 	/* Check for error response */
99 	if (json_resp->error != NULL) {
100 		SPDK_ERRLOG("Unexpected error response\n");
101 		rc = -1;
102 		goto out;
103 	}
104 
105 	assert(json_resp->result);
106 
107 	rc = get_jsonrpc_method_json_parser(&resp, json_resp->result);
108 	if (rc) {
109 		SPDK_ERRLOG("get_jsonrpc_method_json_parser() failed\n");
110 		goto out;
111 	}
112 
113 	for (i = 0; i < (int)resp.method_num; i++) {
114 		if (strcmp(method_name, resp.method_names[i]) == 0) {
115 			rc = 0;
116 			goto out;
117 		}
118 	}
119 
120 	rc = -1;
121 	SPDK_ERRLOG("Method '%s' not found in response\n", method_name);
122 
123 out:
124 	for (i = 0; i < (int)resp.method_num; i++) {
125 		SPDK_NOTICELOG("%s\n", resp.method_names[i]);
126 		free(resp.method_names[i]);
127 	}
128 
129 	spdk_jsonrpc_client_free_response(json_resp);
130 	return rc;
131 }
132 
133 static void
134 rpc_test_method_startup(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
135 {
136 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
137 					 "rpc_test_method_startup(): Method body not implemented");
138 }
139 SPDK_RPC_REGISTER("test_method_startup", rpc_test_method_startup, SPDK_RPC_STARTUP)
140 
141 static void
142 rpc_test_method_runtime(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
143 {
144 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
145 					 "rpc_test_method_runtime(): Method body not implemented");
146 }
147 SPDK_RPC_REGISTER("test_method_runtime", rpc_test_method_runtime, SPDK_RPC_RUNTIME)
148 
149 /* Helper function */
150 static int
151 _sem_timedwait(sem_t *sem, __time_t sec)
152 {
153 	struct timespec timeout;
154 
155 	clock_gettime(CLOCK_REALTIME, &timeout);
156 	timeout.tv_sec += sec;
157 
158 	return sem_timedwait(sem, &timeout);
159 }
160 
161 volatile int g_rpc_server_th_stop;
162 static sem_t g_rpc_server_th_listening;
163 static sem_t g_rpc_server_th_done;
164 
165 static void *
166 rpc_server_th(void *arg)
167 {
168 	int rc;
169 
170 	rc = spdk_rpc_listen(g_rpcsock_addr);
171 	if (rc) {
172 		fprintf(stderr, "spdk_rpc_listen() failed: %d\n", rc);
173 		goto out;
174 	}
175 
176 	sem_post(&g_rpc_server_th_listening);
177 
178 	while (!g_rpc_server_th_stop) {
179 		spdk_rpc_accept();
180 		usleep(50);
181 	}
182 
183 	spdk_rpc_close();
184 out:
185 	sem_post(&g_rpc_server_th_done);
186 
187 	return (void *)(intptr_t)rc;
188 }
189 
190 static sem_t g_rpc_client_th_done;
191 
192 static void *
193 rpc_client_th(void *arg)
194 {
195 	struct spdk_jsonrpc_client *client = NULL;
196 	char *method_name = "get_rpc_methods";
197 	int rc;
198 
199 
200 	rc = _sem_timedwait(&g_rpc_server_th_listening, 2);
201 	if (rc == -1) {
202 		fprintf(stderr, "Timeout waiting for server thread to start listening: %d\n", errno);
203 		goto out;
204 	}
205 
206 	client = spdk_jsonrpc_client_connect(g_rpcsock_addr, g_addr_family);
207 	if (!client) {
208 		fprintf(stderr, "spdk_jsonrpc_client_connect() failed: %d\n", errno);
209 		rc = -1;
210 		goto out;
211 	}
212 
213 	rc = spdk_jsonrpc_client_check_rpc_method(client, method_name);
214 	if (rc) {
215 		fprintf(stderr, "spdk_jsonrpc_client_check_rpc_method() failed: %d\n", errno);
216 		goto out;
217 	}
218 
219 out:
220 	if (client) {
221 		spdk_jsonrpc_client_close(client);
222 	}
223 
224 	sem_post(&g_rpc_client_th_done);
225 	return (void *)(intptr_t)rc;
226 }
227 
228 int main(int argc, char **argv)
229 {
230 	pthread_t srv_tid, client_tid;
231 	int srv_tid_valid;
232 	int client_tid_valid = -1;
233 	int th_rc = INT_MIN;
234 	int rc = 0, err_cnt = 0;
235 
236 	sem_init(&g_rpc_server_th_listening, 0, 0);
237 	sem_init(&g_rpc_server_th_done, 0, 0);
238 	sem_init(&g_rpc_client_th_done, 0, 0);
239 
240 	srv_tid_valid = pthread_create(&srv_tid, NULL, rpc_server_th, NULL);
241 	if (srv_tid_valid != 0) {
242 		fprintf(stderr, "pthread_create() failed to create server thread: %d\n", srv_tid_valid);
243 		goto out;
244 	}
245 
246 	client_tid_valid = pthread_create(&client_tid, NULL, rpc_client_th, NULL);
247 	if (client_tid_valid != 0) {
248 		fprintf(stderr, "pthread_create(): failed to create client thread: %d\n", client_tid_valid);
249 		goto out;
250 	}
251 
252 out:
253 	if (client_tid_valid == 0) {
254 		rc = _sem_timedwait(&g_rpc_client_th_done, JOIN_TIMEOUT_S);
255 		if (rc) {
256 			fprintf(stderr, "failed to join client thread (rc: %d)\n", rc);
257 			err_cnt++;
258 		}
259 
260 		rc = pthread_join(client_tid, (void **)&th_rc);
261 		if (rc) {
262 			fprintf(stderr, "pthread_join() on cliennt thread failed (rc: %d)\n", rc);
263 			err_cnt++;
264 		} else if (th_rc) {
265 			fprintf(stderr, "cliennt thread failed reported failure(thread rc: %d)\n", th_rc);
266 			err_cnt++;
267 		}
268 	}
269 
270 	g_rpc_server_th_stop = 1;
271 
272 	if (srv_tid_valid == 0) {
273 		rc = _sem_timedwait(&g_rpc_server_th_done, JOIN_TIMEOUT_S);
274 		if (rc) {
275 			fprintf(stderr, "server thread failed to exit in %d sec: (rc: %d)\n", JOIN_TIMEOUT_S, rc);
276 			err_cnt++;
277 		}
278 
279 		rc = pthread_join(srv_tid, (void **)&th_rc);
280 		if (rc) {
281 			fprintf(stderr, "pthread_join() on cliennt thread failed (rc: %d)\n", rc);
282 			err_cnt++;
283 		} else if (th_rc) {
284 			fprintf(stderr, "cliennt thread failed reported failure(thread rc: %d)\n", th_rc);
285 			err_cnt++;
286 		}
287 	}
288 
289 	sem_destroy(&g_rpc_server_th_listening);
290 	sem_destroy(&g_rpc_server_th_done);
291 	sem_destroy(&g_rpc_client_th_done);
292 
293 	fprintf(stderr, "%s\n", err_cnt == 0 ? "OK" : "FAILED");
294 	return err_cnt ? EXIT_FAILURE : 0;
295 }
296