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