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