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