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 36 #include "spdk/init.h" 37 #include "spdk/util.h" 38 #include "spdk/file.h" 39 #include "spdk/log.h" 40 #include "spdk/env.h" 41 #include "spdk/thread.h" 42 #include "spdk/jsonrpc.h" 43 #include "spdk/rpc.h" 44 #include "spdk/string.h" 45 46 #include "spdk_internal/event.h" 47 48 #define SPDK_DEBUG_APP_CFG(...) SPDK_DEBUGLOG(app_config, __VA_ARGS__) 49 50 /* JSON configuration format is as follows 51 * 52 * { 53 * "subsystems" : [ <<== *subsystems JSON array 54 * { <<== *subsystems_it array entry pointer (iterator) 55 * "subsystem": "<< SUBSYSTEM NAME >>", 56 * "config": [ <<== *config JSON array 57 * { <<== *config_it array entry pointer (iterator) 58 * "method": "<< METHOD NAME >>", <<== *method 59 * "params": { << PARAMS >> } <<== *params 60 * }, 61 * << MORE "config" ARRY ENTRIES >> 62 * ] 63 * }, 64 * << MORE "subsystems" ARRAY ENTRIES >> 65 * ] 66 * 67 * << ANYTHING ELSE IS IGNORED IN ROOT OBJECT>> 68 * } 69 * 70 */ 71 72 struct load_json_config_ctx; 73 typedef void (*client_resp_handler)(struct load_json_config_ctx *, 74 struct spdk_jsonrpc_client_response *); 75 76 #define RPC_SOCKET_PATH_MAX SPDK_SIZEOF_MEMBER(struct sockaddr_un, sun_path) 77 78 /* 1s connections timeout */ 79 #define RPC_CLIENT_CONNECT_TIMEOUT_US (1U * 1000U * 1000U) 80 81 /* 82 * Currently there is no timeout in SPDK for any RPC command. This result that 83 * we can't put a hard limit during configuration load as it most likely randomly fail. 84 * So just print WARNLOG every 10s. */ 85 #define RPC_CLIENT_REQUEST_TIMEOUT_US (10U * 1000 * 1000) 86 87 struct load_json_config_ctx { 88 /* Thread used during configuration. */ 89 struct spdk_thread *thread; 90 spdk_subsystem_init_fn cb_fn; 91 void *cb_arg; 92 bool stop_on_error; 93 94 /* Current subsystem */ 95 struct spdk_json_val *subsystems; /* "subsystems" array */ 96 struct spdk_json_val *subsystems_it; /* current subsystem array position in "subsystems" array */ 97 98 struct spdk_json_val *subsystem_name; /* current subsystem name */ 99 100 /* Current "config" entry we are processing */ 101 struct spdk_json_val *config; /* "config" array */ 102 struct spdk_json_val *config_it; /* current config position in "config" array */ 103 104 /* Current request id we are sending. */ 105 uint32_t rpc_request_id; 106 107 /* Whole configuration file read and parsed. */ 108 size_t json_data_size; 109 char *json_data; 110 111 size_t values_cnt; 112 struct spdk_json_val *values; 113 114 char rpc_socket_path_temp[RPC_SOCKET_PATH_MAX + 1]; 115 116 struct spdk_jsonrpc_client *client_conn; 117 struct spdk_poller *client_conn_poller; 118 119 client_resp_handler client_resp_cb; 120 121 /* Timeout for current RPC client action. */ 122 uint64_t timeout; 123 }; 124 125 static void app_json_config_load_subsystem(void *_ctx); 126 127 static void 128 app_json_config_load_done(struct load_json_config_ctx *ctx, int rc) 129 { 130 spdk_poller_unregister(&ctx->client_conn_poller); 131 if (ctx->client_conn != NULL) { 132 spdk_jsonrpc_client_close(ctx->client_conn); 133 } 134 135 spdk_rpc_finish(); 136 137 SPDK_DEBUG_APP_CFG("Config load finished with rc %d\n", rc); 138 ctx->cb_fn(rc, ctx->cb_arg); 139 140 free(ctx->json_data); 141 free(ctx->values); 142 free(ctx); 143 } 144 145 static void 146 rpc_client_set_timeout(struct load_json_config_ctx *ctx, uint64_t timeout_us) 147 { 148 ctx->timeout = spdk_get_ticks() + timeout_us * spdk_get_ticks_hz() / (1000 * 1000); 149 } 150 151 static int 152 rpc_client_check_timeout(struct load_json_config_ctx *ctx) 153 { 154 if (ctx->timeout < spdk_get_ticks()) { 155 SPDK_WARNLOG("RPC client command timeout.\n"); 156 return -ETIMEDOUT; 157 } 158 159 return 0; 160 } 161 162 struct json_write_buf { 163 char data[1024]; 164 unsigned cur_off; 165 }; 166 167 static int 168 json_write_stdout(void *cb_ctx, const void *data, size_t size) 169 { 170 struct json_write_buf *buf = cb_ctx; 171 size_t rc; 172 173 rc = snprintf(buf->data + buf->cur_off, sizeof(buf->data) - buf->cur_off, 174 "%s", (const char *)data); 175 if (rc > 0) { 176 buf->cur_off += rc; 177 } 178 return rc == size ? 0 : -1; 179 } 180 181 static int 182 rpc_client_poller(void *arg) 183 { 184 struct load_json_config_ctx *ctx = arg; 185 struct spdk_jsonrpc_client_response *resp; 186 client_resp_handler cb; 187 int rc; 188 189 assert(spdk_get_thread() == ctx->thread); 190 191 rc = spdk_jsonrpc_client_poll(ctx->client_conn, 0); 192 if (rc == 0) { 193 rc = rpc_client_check_timeout(ctx); 194 if (rc == -ETIMEDOUT) { 195 rpc_client_set_timeout(ctx, RPC_CLIENT_REQUEST_TIMEOUT_US); 196 rc = 0; 197 } 198 } 199 200 if (rc == 0) { 201 /* No response yet */ 202 return SPDK_POLLER_BUSY; 203 } else if (rc < 0) { 204 app_json_config_load_done(ctx, rc); 205 return SPDK_POLLER_BUSY; 206 } 207 208 resp = spdk_jsonrpc_client_get_response(ctx->client_conn); 209 assert(resp); 210 211 if (resp->error) { 212 struct json_write_buf buf = {}; 213 struct spdk_json_write_ctx *w = spdk_json_write_begin(json_write_stdout, 214 &buf, SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE); 215 216 if (w == NULL) { 217 SPDK_ERRLOG("error response: (?)\n"); 218 } else { 219 spdk_json_write_val(w, resp->error); 220 spdk_json_write_end(w); 221 SPDK_ERRLOG("error response: \n%s\n", buf.data); 222 } 223 } 224 225 if (resp->error && ctx->stop_on_error) { 226 spdk_jsonrpc_client_free_response(resp); 227 app_json_config_load_done(ctx, -EINVAL); 228 } else { 229 /* We have response so we must have callback for it. */ 230 cb = ctx->client_resp_cb; 231 assert(cb != NULL); 232 233 /* Mark we are done with this handler. */ 234 ctx->client_resp_cb = NULL; 235 cb(ctx, resp); 236 } 237 238 239 return SPDK_POLLER_BUSY; 240 } 241 242 static int 243 rpc_client_connect_poller(void *_ctx) 244 { 245 struct load_json_config_ctx *ctx = _ctx; 246 int rc; 247 248 rc = spdk_jsonrpc_client_poll(ctx->client_conn, 0); 249 if (rc != -ENOTCONN) { 250 /* We are connected. Start regular poller and issue first request */ 251 spdk_poller_unregister(&ctx->client_conn_poller); 252 ctx->client_conn_poller = SPDK_POLLER_REGISTER(rpc_client_poller, ctx, 100); 253 app_json_config_load_subsystem(ctx); 254 } else { 255 rc = rpc_client_check_timeout(ctx); 256 if (rc) { 257 app_json_config_load_done(ctx, rc); 258 } 259 260 return SPDK_POLLER_IDLE; 261 } 262 263 return SPDK_POLLER_BUSY; 264 } 265 266 static int 267 client_send_request(struct load_json_config_ctx *ctx, struct spdk_jsonrpc_client_request *request, 268 client_resp_handler client_resp_cb) 269 { 270 int rc; 271 272 assert(spdk_get_thread() == ctx->thread); 273 274 ctx->client_resp_cb = client_resp_cb; 275 rpc_client_set_timeout(ctx, RPC_CLIENT_REQUEST_TIMEOUT_US); 276 rc = spdk_jsonrpc_client_send_request(ctx->client_conn, request); 277 278 if (rc) { 279 SPDK_DEBUG_APP_CFG("Sending request to client failed (%d)\n", rc); 280 } 281 282 return rc; 283 } 284 285 static int 286 cap_string(const struct spdk_json_val *val, void *out) 287 { 288 const struct spdk_json_val **vptr = out; 289 290 if (val->type != SPDK_JSON_VAL_STRING) { 291 return -EINVAL; 292 } 293 294 *vptr = val; 295 return 0; 296 } 297 298 static int 299 cap_object(const struct spdk_json_val *val, void *out) 300 { 301 const struct spdk_json_val **vptr = out; 302 303 if (val->type != SPDK_JSON_VAL_OBJECT_BEGIN) { 304 return -EINVAL; 305 } 306 307 *vptr = val; 308 return 0; 309 } 310 311 312 static int 313 cap_array_or_null(const struct spdk_json_val *val, void *out) 314 { 315 const struct spdk_json_val **vptr = out; 316 317 if (val->type != SPDK_JSON_VAL_ARRAY_BEGIN && val->type != SPDK_JSON_VAL_NULL) { 318 return -EINVAL; 319 } 320 321 *vptr = val; 322 return 0; 323 } 324 325 struct config_entry { 326 char *method; 327 struct spdk_json_val *params; 328 }; 329 330 static struct spdk_json_object_decoder jsonrpc_cmd_decoders[] = { 331 {"method", offsetof(struct config_entry, method), spdk_json_decode_string}, 332 {"params", offsetof(struct config_entry, params), cap_object, true} 333 }; 334 335 static void app_json_config_load_subsystem_config_entry(void *_ctx); 336 337 static void 338 app_json_config_load_subsystem_config_entry_next(struct load_json_config_ctx *ctx, 339 struct spdk_jsonrpc_client_response *resp) 340 { 341 /* Don't care about the response */ 342 spdk_jsonrpc_client_free_response(resp); 343 344 ctx->config_it = spdk_json_next(ctx->config_it); 345 app_json_config_load_subsystem_config_entry(ctx); 346 } 347 348 /* Load "config" entry */ 349 static void 350 app_json_config_load_subsystem_config_entry(void *_ctx) 351 { 352 struct load_json_config_ctx *ctx = _ctx; 353 struct spdk_jsonrpc_client_request *rpc_request; 354 struct spdk_json_write_ctx *w; 355 struct config_entry cfg = {}; 356 struct spdk_json_val *params_end; 357 size_t params_len = 0; 358 int rc; 359 360 if (ctx->config_it == NULL) { 361 SPDK_DEBUG_APP_CFG("Subsystem '%.*s': configuration done.\n", ctx->subsystem_name->len, 362 (char *)ctx->subsystem_name->start); 363 ctx->subsystems_it = spdk_json_next(ctx->subsystems_it); 364 /* Invoke later to avoid recurrency */ 365 spdk_thread_send_msg(ctx->thread, app_json_config_load_subsystem, ctx); 366 return; 367 } 368 369 if (spdk_json_decode_object(ctx->config_it, jsonrpc_cmd_decoders, 370 SPDK_COUNTOF(jsonrpc_cmd_decoders), &cfg)) { 371 SPDK_ERRLOG("Failed to decode config entry\n"); 372 app_json_config_load_done(ctx, -EINVAL); 373 goto out; 374 } 375 376 rc = spdk_rpc_is_method_allowed(cfg.method, spdk_rpc_get_state()); 377 if (rc == -EPERM) { 378 SPDK_DEBUG_APP_CFG("Method '%s' not allowed -> skipping\n", cfg.method); 379 /* Invoke later to avoid recurrency */ 380 ctx->config_it = spdk_json_next(ctx->config_it); 381 spdk_thread_send_msg(ctx->thread, app_json_config_load_subsystem_config_entry, ctx); 382 goto out; 383 } 384 385 SPDK_DEBUG_APP_CFG("\tmethod: %s\n", cfg.method); 386 387 if (cfg.params) { 388 /* Get _END by skipping params and going back by one element. */ 389 params_end = cfg.params + spdk_json_val_len(cfg.params) - 1; 390 391 /* Need to add one character to include '}' */ 392 params_len = params_end->start - cfg.params->start + 1; 393 394 SPDK_DEBUG_APP_CFG("\tparams: %.*s\n", (int)params_len, (char *)cfg.params->start); 395 } 396 397 rpc_request = spdk_jsonrpc_client_create_request(); 398 if (!rpc_request) { 399 app_json_config_load_done(ctx, -errno); 400 goto out; 401 } 402 403 w = spdk_jsonrpc_begin_request(rpc_request, ctx->rpc_request_id, NULL); 404 if (!w) { 405 spdk_jsonrpc_client_free_request(rpc_request); 406 app_json_config_load_done(ctx, -ENOMEM); 407 goto out; 408 } 409 410 spdk_json_write_named_string(w, "method", cfg.method); 411 412 if (cfg.params) { 413 /* No need to parse "params". Just dump the whole content of "params" 414 * directly into the request and let the remote side verify it. */ 415 spdk_json_write_name(w, "params"); 416 spdk_json_write_val_raw(w, cfg.params->start, params_len); 417 } 418 419 spdk_jsonrpc_end_request(rpc_request, w); 420 421 rc = client_send_request(ctx, rpc_request, app_json_config_load_subsystem_config_entry_next); 422 if (rc != 0) { 423 app_json_config_load_done(ctx, -rc); 424 goto out; 425 } 426 out: 427 free(cfg.method); 428 } 429 430 static void 431 subsystem_init_done(int rc, void *arg1) 432 { 433 struct load_json_config_ctx *ctx = arg1; 434 435 if (rc) { 436 app_json_config_load_done(ctx, rc); 437 return; 438 } 439 440 spdk_rpc_set_state(SPDK_RPC_RUNTIME); 441 /* Another round. This time for RUNTIME methods */ 442 SPDK_DEBUG_APP_CFG("'framework_start_init' done - continuing configuration\n"); 443 444 assert(ctx != NULL); 445 if (ctx->subsystems) { 446 ctx->subsystems_it = spdk_json_array_first(ctx->subsystems); 447 } 448 449 app_json_config_load_subsystem(ctx); 450 } 451 452 static struct spdk_json_object_decoder subsystem_decoders[] = { 453 {"subsystem", offsetof(struct load_json_config_ctx, subsystem_name), cap_string}, 454 {"config", offsetof(struct load_json_config_ctx, config), cap_array_or_null} 455 }; 456 457 /* 458 * Start loading subsystem pointed by ctx->subsystems_it. This must point to the 459 * beginning of the "subsystem" object in "subsystems" array or be NULL. If it is 460 * NULL then no more subsystems to load. 461 * 462 * There are two iterations: 463 * 464 * In first iteration only STARTUP RPC methods are used, other methods are ignored. When 465 * allsubsystems are walked the ctx->subsystems_it became NULL and "framework_start_init" 466 * is called to let the SPDK move to RUNTIME state (initialize all subsystems) and 467 * second iteration begins. 468 * 469 * In second iteration "subsystems" array is walked through again, this time only 470 * RUNTIME RPC methods are used. When ctx->subsystems_it became NULL second time it 471 * indicate that there is no more subsystems to load. The cb_fn is called to finish 472 * configuration. 473 */ 474 static void 475 app_json_config_load_subsystem(void *_ctx) 476 { 477 struct load_json_config_ctx *ctx = _ctx; 478 479 if (ctx->subsystems_it == NULL) { 480 if (spdk_rpc_get_state() == SPDK_RPC_STARTUP) { 481 SPDK_DEBUG_APP_CFG("No more entries for current state, calling 'framework_start_init'\n"); 482 spdk_subsystem_init(subsystem_init_done, ctx); 483 } else { 484 app_json_config_load_done(ctx, 0); 485 } 486 487 return; 488 } 489 490 /* Capture subsystem name and config array */ 491 if (spdk_json_decode_object(ctx->subsystems_it, subsystem_decoders, 492 SPDK_COUNTOF(subsystem_decoders), ctx)) { 493 SPDK_ERRLOG("Failed to parse subsystem configuration\n"); 494 app_json_config_load_done(ctx, -EINVAL); 495 return; 496 } 497 498 SPDK_DEBUG_APP_CFG("Loading subsystem '%.*s' configuration\n", ctx->subsystem_name->len, 499 (char *)ctx->subsystem_name->start); 500 501 /* Get 'config' array first configuration entry */ 502 ctx->config_it = spdk_json_array_first(ctx->config); 503 app_json_config_load_subsystem_config_entry(ctx); 504 } 505 506 static void * 507 read_file(const char *filename, size_t *size) 508 { 509 FILE *file = fopen(filename, "r"); 510 void *data; 511 512 if (file == NULL) { 513 /* errno is set by fopen */ 514 return NULL; 515 } 516 517 data = spdk_posix_file_load(file, size); 518 fclose(file); 519 return data; 520 } 521 522 static int 523 app_json_config_read(const char *config_file, struct load_json_config_ctx *ctx) 524 { 525 struct spdk_json_val *values = NULL; 526 void *json = NULL, *end; 527 ssize_t values_cnt, rc; 528 size_t json_size; 529 530 json = read_file(config_file, &json_size); 531 if (!json) { 532 SPDK_ERRLOG("Read JSON configuration file %s failed: %s\n", 533 config_file, spdk_strerror(errno)); 534 return -errno; 535 } 536 537 rc = spdk_json_parse(json, json_size, NULL, 0, &end, 538 SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); 539 if (rc < 0) { 540 SPDK_ERRLOG("Parsing JSON configuration failed (%zd)\n", rc); 541 goto err; 542 } 543 544 values_cnt = rc; 545 values = calloc(values_cnt, sizeof(struct spdk_json_val)); 546 if (values == NULL) { 547 SPDK_ERRLOG("Out of memory\n"); 548 goto err; 549 } 550 551 rc = spdk_json_parse(json, json_size, values, values_cnt, &end, 552 SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); 553 if (rc != values_cnt) { 554 SPDK_ERRLOG("Parsing JSON configuration failed (%zd)\n", rc); 555 goto err; 556 } 557 558 ctx->json_data = json; 559 ctx->json_data_size = json_size; 560 561 ctx->values = values; 562 ctx->values_cnt = values_cnt; 563 564 return 0; 565 err: 566 free(json); 567 free(values); 568 return rc; 569 } 570 571 void 572 spdk_subsystem_init_from_json_config(const char *json_config_file, const char *rpc_addr, 573 spdk_subsystem_init_fn cb_fn, void *cb_arg, 574 bool stop_on_error) 575 { 576 struct load_json_config_ctx *ctx = calloc(1, sizeof(*ctx)); 577 int rc; 578 579 assert(cb_fn); 580 if (!ctx) { 581 cb_fn(-ENOMEM, cb_arg); 582 return; 583 } 584 585 ctx->cb_fn = cb_fn; 586 ctx->cb_arg = cb_arg; 587 ctx->stop_on_error = stop_on_error; 588 ctx->thread = spdk_get_thread(); 589 590 rc = app_json_config_read(json_config_file, ctx); 591 if (rc) { 592 goto fail; 593 } 594 595 /* Capture subsystems array */ 596 rc = spdk_json_find_array(ctx->values, "subsystems", NULL, &ctx->subsystems); 597 if (rc) { 598 SPDK_WARNLOG("No 'subsystems' key JSON configuration file.\n"); 599 } else { 600 /* Get first subsystem */ 601 ctx->subsystems_it = spdk_json_array_first(ctx->subsystems); 602 if (ctx->subsystems_it == NULL) { 603 SPDK_NOTICELOG("'subsystems' configuration is empty\n"); 604 } 605 } 606 607 /* If rpc_addr is not an Unix socket use default address as prefix. */ 608 if (rpc_addr == NULL || rpc_addr[0] != '/') { 609 rpc_addr = SPDK_DEFAULT_RPC_ADDR; 610 } 611 612 /* FIXME: rpc client should use socketpair() instead of this temporary socket nonsense */ 613 rc = snprintf(ctx->rpc_socket_path_temp, sizeof(ctx->rpc_socket_path_temp), "%s.%d_config", 614 rpc_addr, getpid()); 615 if (rc >= (int)sizeof(ctx->rpc_socket_path_temp)) { 616 SPDK_ERRLOG("Socket name create failed\n"); 617 goto fail; 618 } 619 620 rc = spdk_rpc_initialize(ctx->rpc_socket_path_temp); 621 if (rc) { 622 goto fail; 623 } 624 625 ctx->client_conn = spdk_jsonrpc_client_connect(ctx->rpc_socket_path_temp, AF_UNIX); 626 if (ctx->client_conn == NULL) { 627 SPDK_ERRLOG("Failed to connect to '%s'\n", ctx->rpc_socket_path_temp); 628 goto fail; 629 } 630 631 rpc_client_set_timeout(ctx, RPC_CLIENT_CONNECT_TIMEOUT_US); 632 ctx->client_conn_poller = SPDK_POLLER_REGISTER(rpc_client_connect_poller, ctx, 100); 633 return; 634 635 fail: 636 app_json_config_load_done(ctx, -EINVAL); 637 } 638 639 SPDK_LOG_REGISTER_COMPONENT(app_config) 640