1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2021 Intel Corporation.
3 * All rights reserved.
4 * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5 */
6
7 #include "spdk/stdinc.h"
8 #include "spdk_internal/cunit.h"
9 #include "spdk/jsonrpc.h"
10 #include "spdk_internal/mock.h"
11 #include "common/lib/test_env.c"
12 #include "spdk/log.h"
13
14 #include "rpc/rpc.c"
15
16 #include "unit/lib/json_mock.c"
17
18 static int g_rpc_err;
19 void fn_rpc_method_handler(struct spdk_jsonrpc_request *request,
20 const struct spdk_json_val *params);
21
22 DEFINE_STUB_V(spdk_jsonrpc_end_result, (struct spdk_jsonrpc_request *request,
23 struct spdk_json_write_ctx *w));
24 DEFINE_STUB(spdk_jsonrpc_begin_result, struct spdk_json_write_ctx *,
25 (struct spdk_jsonrpc_request *request), (void *)1);
26 DEFINE_STUB(spdk_json_decode_bool, int, (const struct spdk_json_val *val, void *out), 0);
27 DEFINE_STUB(spdk_jsonrpc_server_listen, struct spdk_jsonrpc_server *, (int domain, int protocol,
28 struct sockaddr *listen_addr, socklen_t addrlen, spdk_jsonrpc_handle_request_fn handle_request),
29 (struct spdk_jsonrpc_server *)0Xdeaddead);
30 DEFINE_STUB(spdk_jsonrpc_server_poll, int, (struct spdk_jsonrpc_server *server), 0);
31 DEFINE_STUB_V(spdk_jsonrpc_server_shutdown, (struct spdk_jsonrpc_server *server));
32
33 DECLARE_WRAPPER(open, int, (const char *pathname, int flags, mode_t mode));
34 DECLARE_WRAPPER(close, int, (int fd));
35 DECLARE_WRAPPER(flock, int, (int fd, int operation));
36 DEFINE_WRAPPER(open, int, (const char *pathname, int flags, mode_t mode), (pathname, flags, mode));
37 DEFINE_WRAPPER(close, int, (int fd), (fd));
38 DEFINE_WRAPPER(flock, int, (int fd, int operation), (fd, operation));
39
40 int
spdk_json_decode_object(const struct spdk_json_val * values,const struct spdk_json_object_decoder * decoders,size_t num_decoders,void * out)41 spdk_json_decode_object(const struct spdk_json_val *values,
42 const struct spdk_json_object_decoder *decoders, size_t num_decoders, void *out)
43 {
44 if (values ->type == SPDK_JSON_VAL_INVALID) {
45 return 1;
46 }
47 return 0;
48 }
49
50 bool
spdk_json_strequal(const struct spdk_json_val * val,const char * str)51 spdk_json_strequal(const struct spdk_json_val *val, const char *str)
52 {
53 size_t len;
54
55 if (val->type != SPDK_JSON_VAL_STRING && val->type != SPDK_JSON_VAL_NAME) {
56 return false;
57 }
58
59 len = strlen(str);
60 if (val->len != len) {
61 return false;
62 }
63
64 return memcmp(val->start, str, len) == 0;
65 }
66
67 void
spdk_jsonrpc_send_error_response(struct spdk_jsonrpc_request * request,int error_code,const char * msg)68 spdk_jsonrpc_send_error_response(struct spdk_jsonrpc_request *request,
69 int error_code, const char *msg)
70 {
71 g_rpc_err = error_code;
72 }
73
74 void
spdk_jsonrpc_send_error_response_fmt(struct spdk_jsonrpc_request * request,int error_code,const char * fmt,...)75 spdk_jsonrpc_send_error_response_fmt(struct spdk_jsonrpc_request *request,
76 int error_code, const char *fmt, ...)
77 {
78 g_rpc_err = error_code;
79 }
80
81 void
fn_rpc_method_handler(struct spdk_jsonrpc_request * request,const struct spdk_json_val * params)82 fn_rpc_method_handler(struct spdk_jsonrpc_request *request,
83 const struct spdk_json_val *params)
84 {
85 g_rpc_err = 0;
86 }
87
88 static void
test_jsonrpc_handler(void)89 test_jsonrpc_handler(void)
90 {
91 struct spdk_jsonrpc_request *request = (struct spdk_jsonrpc_request *)0xdeadbeef;
92 struct spdk_json_val method = {};
93 struct spdk_json_val params = {};
94 char *str = "test";
95 struct spdk_rpc_method m = {
96 .name = "test",
97 };
98
99 struct spdk_rpc_method is_alias_of = {
100 .name = "aliastest",
101 .is_deprecated = false,
102 .deprecation_warning_printed = false,
103 .func = fn_rpc_method_handler,
104 .state_mask = SPDK_RPC_STARTUP,
105 };
106
107 /* Case 1: Method not found */
108 method.type = SPDK_JSON_VAL_INVALID;
109 jsonrpc_handler(request, &method, ¶ms);
110 CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND);
111
112 /* Case 2: Method is alias */
113 method.type = SPDK_JSON_VAL_STRING;
114 method.start = str;
115 method.len = 4;
116 m.is_alias_of = &is_alias_of;
117 m.is_deprecated = true;
118 m.deprecation_warning_printed = false;
119 m.state_mask = SPDK_RPC_STARTUP;
120 SLIST_INSERT_HEAD(&g_rpc_methods, &m, slist);
121
122 /* m->state_mask & g_rpc_state == g_rpc_state */
123 g_rpc_err = -1;
124 g_rpc_state = SPDK_RPC_STARTUP;
125 jsonrpc_handler(request, &method, ¶ms);
126 CU_ASSERT(g_rpc_err == 0);
127
128 /* g_rpc_state == SPDK_RPC_STARTUP */
129 is_alias_of.state_mask = SPDK_RPC_RUNTIME;
130 g_rpc_err = -1;
131 g_rpc_state = SPDK_RPC_STARTUP;
132 jsonrpc_handler(request, &method, ¶ms);
133 CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_INVALID_STATE);
134
135 /* SPDK_RPC_RUNTIME is invalid for the aliastest RPC */
136 is_alias_of.state_mask = SPDK_RPC_STARTUP;
137 g_rpc_err = -1;
138 g_rpc_state = SPDK_RPC_RUNTIME;
139 jsonrpc_handler(request, &method, ¶ms);
140 CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_INVALID_STATE);
141
142 SLIST_REMOVE_HEAD(&g_rpc_methods, slist);
143 }
144
145 static void
test_spdk_rpc_is_method_allowed(void)146 test_spdk_rpc_is_method_allowed(void)
147 {
148 const char method[] = "test";
149 uint32_t state_mask = SPDK_RPC_STARTUP, m_state_mask;
150 struct spdk_rpc_method m = {};
151 int rc = 0;
152
153 /* Case 1: Expect return -EPERM */
154 m.name = method;
155 m.state_mask = SPDK_RPC_RUNTIME;
156 SLIST_INSERT_HEAD(&g_rpc_methods, &m, slist);
157 rc = spdk_rpc_is_method_allowed(method, state_mask);
158 CU_ASSERT(rc == -EPERM);
159 rc = spdk_rpc_get_method_state_mask(method, &m_state_mask);
160 CU_ASSERT(rc == 0);
161 CU_ASSERT(m_state_mask == m.state_mask);
162
163 /* Case 2: Expect return 0 */
164 state_mask = SPDK_RPC_RUNTIME;
165 rc = spdk_rpc_is_method_allowed(method, state_mask);
166 CU_ASSERT(rc == 0);
167
168 /* Case 3: Expect return -ENOENT */
169 SLIST_REMOVE_HEAD(&g_rpc_methods, slist);
170 rc = spdk_rpc_is_method_allowed(method, state_mask);
171 CU_ASSERT(rc == -ENOENT);
172 rc = spdk_rpc_get_method_state_mask(method, &m_state_mask);
173 CU_ASSERT(rc == -ENOENT);
174 }
175
176 static void
test_rpc_get_methods(void)177 test_rpc_get_methods(void)
178 {
179 struct spdk_jsonrpc_request *request = (struct spdk_jsonrpc_request *)0xbeefbeef;
180 struct spdk_json_val params = {};
181 struct spdk_rpc_method m = {};
182
183 /* Case 1: spdk_json_decode_object failed */
184 g_rpc_err = -1;
185 params.type = SPDK_JSON_VAL_INVALID;
186 rpc_get_methods(request, ¶ms);
187 CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_INVALID_PARAMS);
188
189 /* Case 2: Expect pass */
190 params.type = SPDK_JSON_VAL_TRUE;
191 m.state_mask = SPDK_RPC_RUNTIME;
192 g_rpc_state = SPDK_RPC_STARTUP;
193 SLIST_INSERT_HEAD(&g_rpc_methods, &m, slist);
194 rpc_get_methods(request, ¶ms);
195 SLIST_REMOVE_HEAD(&g_rpc_methods, slist);
196 }
197
198 static void
test_rpc_spdk_get_version(void)199 test_rpc_spdk_get_version(void)
200 {
201 struct spdk_jsonrpc_request *request = (struct spdk_jsonrpc_request *)0xdeadbeef;
202 struct spdk_json_val params = {};
203
204 /* Case 1: spdk_get_version method requires no parameters */
205 g_rpc_err = -1;
206 params.type = SPDK_JSON_VAL_INVALID;
207 rpc_spdk_get_version(request, ¶ms);
208 CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_INVALID_PARAMS);
209
210 /* Case 2: Expect pass */
211 rpc_spdk_get_version(request, NULL);
212 }
213
214 static void
test_spdk_rpc_listen_close(void)215 test_spdk_rpc_listen_close(void)
216 {
217 const char listen_addr[128] = "/var/tmp/spdk-rpc-ut.sock";
218 char rpc_lock_path[128] = {};
219
220 MOCK_SET(open, 1);
221 MOCK_SET(close, 0);
222 MOCK_SET(flock, 0);
223
224 spdk_rpc_listen(listen_addr);
225 snprintf(rpc_lock_path, sizeof(g_rpc_server.lock_path), "%s.lock",
226 g_rpc_server.listen_addr_unix.sun_path);
227
228 CU_ASSERT(g_rpc_server.listen_addr_unix.sun_family == AF_UNIX);
229 CU_ASSERT(strcmp(g_rpc_server.listen_addr_unix.sun_path, listen_addr) == 0);
230 CU_ASSERT(strcmp(g_rpc_server.lock_path, rpc_lock_path) == 0);
231 CU_ASSERT(g_rpc_server.jsonrpc_server == (struct spdk_jsonrpc_server *)0Xdeaddead);
232
233 spdk_rpc_close();
234
235 CU_ASSERT(g_rpc_server.listen_addr_unix.sun_path[0] == '\0');
236 CU_ASSERT(g_rpc_server.jsonrpc_server == NULL);
237 CU_ASSERT(g_rpc_server.lock_fd == -1);
238 CU_ASSERT(g_rpc_server.lock_path[0] == '\0');
239
240 MOCK_CLEAR(open);
241 MOCK_CLEAR(close);
242 MOCK_CLEAR(flock);
243 }
244
245 static void
test_rpc_run_multiple_servers(void)246 test_rpc_run_multiple_servers(void)
247 {
248 const char listen_addr1[128] = "/var/tmp/spdk-rpc-ut.sock1";
249 const char listen_addr2[128] = "/var/tmp/spdk-rpc-ut.sock2";
250 const char listen_addr3[128] = "/var/tmp/spdk-rpc-ut.sock3";
251 struct spdk_rpc_server *rpc_server1, *rpc_server2, *rpc_server3;
252
253 MOCK_SET(open, 1);
254 MOCK_SET(close, 0);
255 MOCK_SET(flock, 0);
256
257 rpc_server1 = spdk_rpc_server_listen(listen_addr1);
258 CU_ASSERT(rpc_server1 != NULL);
259 rpc_server2 = spdk_rpc_server_listen(listen_addr2);
260 CU_ASSERT(rpc_server2 != NULL);
261 rpc_server3 = spdk_rpc_server_listen(listen_addr3);
262 CU_ASSERT(rpc_server3 != NULL);
263
264 spdk_rpc_server_close(rpc_server1);
265 spdk_rpc_server_close(rpc_server2);
266 spdk_rpc_server_close(rpc_server3);
267
268 MOCK_CLEAR(open);
269 MOCK_CLEAR(close);
270 MOCK_CLEAR(flock);
271 }
272
273 int
main(int argc,char ** argv)274 main(int argc, char **argv)
275 {
276 CU_pSuite suite = NULL;
277 unsigned int num_failures;
278
279 CU_initialize_registry();
280
281 suite = CU_add_suite("rpc", NULL, NULL);
282
283 CU_ADD_TEST(suite, test_jsonrpc_handler);
284 CU_ADD_TEST(suite, test_spdk_rpc_is_method_allowed);
285 CU_ADD_TEST(suite, test_rpc_get_methods);
286 CU_ADD_TEST(suite, test_rpc_spdk_get_version);
287 CU_ADD_TEST(suite, test_spdk_rpc_listen_close);
288 CU_ADD_TEST(suite, test_rpc_run_multiple_servers);
289
290 num_failures = spdk_ut_run_tests(argc, argv, NULL);
291 CU_cleanup_registry();
292 return num_failures;
293 }
294