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 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 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 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 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 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 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 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 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 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 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 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 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