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
_rpc_client_wait_for_response(struct spdk_jsonrpc_client * client)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
get_jsonrpc_method_json_parser(struct get_jsonrpc_methods_resp * resp,const struct spdk_json_val * result)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
spdk_jsonrpc_client_check_rpc_method(struct spdk_jsonrpc_client * client,char * method_name)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
spdk_jsonrpc_client_check_null_params_method(struct spdk_jsonrpc_client * client)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
rpc_test_method_startup(struct spdk_jsonrpc_request * request,const struct spdk_json_val * params)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
rpc_test_method_runtime(struct spdk_jsonrpc_request * request,const struct spdk_json_val * params)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
rpc_test_method_null_params(struct spdk_jsonrpc_request * request,const struct spdk_json_val * params)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
rpc_test_conn_close_cb(struct spdk_jsonrpc_server_conn * conn,void * ctx)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
rpc_hook_conn_close(struct spdk_jsonrpc_request * request,const struct spdk_json_val * params)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
spdk_jsonrpc_client_hook_conn_close(struct spdk_jsonrpc_client * client)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 *
rpc_server_th(void * arg)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 *
rpc_client_th(void * arg)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
main(int argc,char ** argv)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