xref: /spdk/examples/blob/cli/blobcli.c (revision b37db06935181fd0e8f5592a96d860040abaa201)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2017 Intel Corporation.
38fbbfccdSPaul Luse  *   All rights reserved.
4ab2aad85SMike Gerdts  *   Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
58fbbfccdSPaul Luse  */
68fbbfccdSPaul Luse 
78fbbfccdSPaul Luse #include "spdk/stdinc.h"
88fbbfccdSPaul Luse 
98fbbfccdSPaul Luse #include "spdk/bdev.h"
108fbbfccdSPaul Luse #include "spdk/env.h"
118fbbfccdSPaul Luse #include "spdk/event.h"
128fbbfccdSPaul Luse #include "spdk/blob_bdev.h"
138fbbfccdSPaul Luse #include "spdk/blob.h"
148fbbfccdSPaul Luse #include "spdk/log.h"
158fbbfccdSPaul Luse #include "spdk/version.h"
16c5e63c71SPaul Luse #include "spdk/string.h"
17d1d22046SJim Harris #include "spdk/uuid.h"
188fbbfccdSPaul Luse 
198fbbfccdSPaul Luse /*
20866f15b3SPaul Luse  * The following is not a public header file, but the CLI does expose
21b7876f9aSJosh Soref  * some internals of blobstore for dev/debug purposes so we
228fbbfccdSPaul Luse  * include it here.
238fbbfccdSPaul Luse  */
248fbbfccdSPaul Luse #include "../lib/blob/blobstore.h"
25deb8ee5cSBen Walker static void cli_start(void *arg1);
268fbbfccdSPaul Luse 
278fbbfccdSPaul Luse static const char *program_name = "blobcli";
286a41e84cSWANGHAILIANG /* default name for .json file, any name can be used however with -j switch */
296a41e84cSWANGHAILIANG static const char *program_conf = "blobcli.json";
308fbbfccdSPaul Luse 
31c5e63c71SPaul Luse /*
32c5e63c71SPaul Luse  * CMD mode runs one command at a time which can be annoying as the init takes
33c5e63c71SPaul Luse  * a few seconds, so the shell mode, invoked with -S, does the init once and gives
34e5044f79SPaul Luse  * the user an interactive shell instead. With script mode init is also done just
35e5044f79SPaul Luse  * once.
36c5e63c71SPaul Luse  */
37c5e63c71SPaul Luse enum cli_mode_type {
38c5e63c71SPaul Luse 	CLI_MODE_CMD,
39e5044f79SPaul Luse 	CLI_MODE_SHELL,
40e5044f79SPaul Luse 	CLI_MODE_SCRIPT
41c5e63c71SPaul Luse };
42c5e63c71SPaul Luse 
438fbbfccdSPaul Luse enum cli_action_type {
44c5e63c71SPaul Luse 	CLI_NONE,
454952f254SDaniel Verkamp 	CLI_IMPORT_BLOB,
464952f254SDaniel Verkamp 	CLI_DUMP_BLOB,
478fbbfccdSPaul Luse 	CLI_FILL,
488fbbfccdSPaul Luse 	CLI_REM_XATTR,
498fbbfccdSPaul Luse 	CLI_SET_XATTR,
508fbbfccdSPaul Luse 	CLI_SET_SUPER,
518fbbfccdSPaul Luse 	CLI_SHOW_BS,
528fbbfccdSPaul Luse 	CLI_SHOW_BLOB,
538fbbfccdSPaul Luse 	CLI_CREATE_BLOB,
548fbbfccdSPaul Luse 	CLI_LIST_BDEVS,
558fbbfccdSPaul Luse 	CLI_LIST_BLOBS,
56c5e63c71SPaul Luse 	CLI_INIT_BS,
57d1d22046SJim Harris 	CLI_DUMP_BS,
58c5e63c71SPaul Luse 	CLI_SHELL_EXIT,
59e5044f79SPaul Luse 	CLI_HELP,
60ff4fd58bSMike Gerdts 	CLI_RECOVER,
613453391bSMike Gerdts 	CLI_DELETE_BLOB,
628fbbfccdSPaul Luse };
638fbbfccdSPaul Luse 
64866f15b3SPaul Luse #define BUFSIZE 255
65e5044f79SPaul Luse #define MAX_ARGS 16
66866f15b3SPaul Luse #define ALIGN_4K 4096
676751b44bSTomasz Zawadzki #define STARTING_IO_UNIT 0
686751b44bSTomasz Zawadzki #define NUM_IO_UNITS 1
69e5044f79SPaul Luse 
70c5e63c71SPaul Luse /*
71c5e63c71SPaul Luse  * The CLI uses the SPDK app framework so is async and callback driven. A
72c5e63c71SPaul Luse  * pointer to this structure is passed to SPDK calls and returned in the
73c5e63c71SPaul Luse  * callbacks for easy access to all the info we may need.
74c5e63c71SPaul Luse  */
758fbbfccdSPaul Luse struct cli_context_t {
768fbbfccdSPaul Luse 	struct spdk_blob_store *bs;
778fbbfccdSPaul Luse 	struct spdk_blob *blob;
78c5e63c71SPaul Luse 	struct spdk_bs_dev *bs_dev;
798fbbfccdSPaul Luse 	spdk_blob_id blobid;
808fbbfccdSPaul Luse 	spdk_blob_id superid;
818fbbfccdSPaul Luse 	struct spdk_io_channel *channel;
828fbbfccdSPaul Luse 	uint8_t *buff;
838fbbfccdSPaul Luse 	uint64_t page_size;
846751b44bSTomasz Zawadzki 	uint64_t io_unit_size;
856751b44bSTomasz Zawadzki 	uint64_t io_unit_count;
866751b44bSTomasz Zawadzki 	uint64_t blob_io_units;
878fbbfccdSPaul Luse 	uint64_t bytes_so_far;
888fbbfccdSPaul Luse 	FILE *fp;
898fbbfccdSPaul Luse 	enum cli_action_type action;
908fbbfccdSPaul Luse 	char key[BUFSIZE + 1];
918fbbfccdSPaul Luse 	char value[BUFSIZE + 1];
928fbbfccdSPaul Luse 	char file[BUFSIZE + 1];
938fbbfccdSPaul Luse 	uint64_t filesize;
948fbbfccdSPaul Luse 	int fill_value;
95cfa4cae0SPaul Luse 	char bdev_name[BUFSIZE];
968fbbfccdSPaul Luse 	int rc;
978fbbfccdSPaul Luse 	int num_clusters;
98c5e63c71SPaul Luse 	enum cli_mode_type cli_mode;
99c5e63c71SPaul Luse 	const char *config_file;
100c5e63c71SPaul Luse 	int argc;
101c5e63c71SPaul Luse 	char *argv[MAX_ARGS];
102c5e63c71SPaul Luse 	bool app_started;
103e5044f79SPaul Luse 	char script_file[BUFSIZE + 1];
1048fbbfccdSPaul Luse };
1058fbbfccdSPaul Luse 
106e5044f79SPaul Luse /* we store a bunch of stuff in a global struct for use by scripting mode */
107e5044f79SPaul Luse #define MAX_SCRIPT_LINES 64
108e5044f79SPaul Luse #define MAX_SCRIPT_BLOBS 16
109e5044f79SPaul Luse struct cli_script_t {
110e5044f79SPaul Luse 	spdk_blob_id blobid[MAX_SCRIPT_BLOBS];
111e5044f79SPaul Luse 	int blobid_idx;
112e5044f79SPaul Luse 	int max_index;
113e5044f79SPaul Luse 	int cmdline_idx;
114e5044f79SPaul Luse 	bool ignore_errors;
115e5044f79SPaul Luse 	char *cmdline[MAX_SCRIPT_LINES];
116e5044f79SPaul Luse };
117e5044f79SPaul Luse struct cli_script_t g_script;
118e5044f79SPaul Luse 
1198fbbfccdSPaul Luse /*
120c5e63c71SPaul Luse  * Common printing of commands for CLI and shell modes.
121c5e63c71SPaul Luse  */
122c5e63c71SPaul Luse static void
123e5044f79SPaul Luse print_cmds(void)
124c5e63c71SPaul Luse {
125c5e63c71SPaul Luse 	printf("\nCommands include:\n");
126cfa4cae0SPaul Luse 	printf("\t-b bdev - name of the block device to use (example: Nvme0n1)\n");
127c5e63c71SPaul Luse 	printf("\t-d <blobid> filename - dump contents of a blob to a file\n");
128d1d22046SJim Harris 	printf("\t-D - dump metadata contents of an existing blobstore\n");
129c5e63c71SPaul Luse 	printf("\t-f <blobid> value - fill a blob with a decimal value\n");
130c5e63c71SPaul Luse 	printf("\t-h - this help screen\n");
131c5e63c71SPaul Luse 	printf("\t-i - initialize a blobstore\n");
132c5e63c71SPaul Luse 	printf("\t-l bdevs | blobs - list either available bdevs or existing blobs\n");
133c5e63c71SPaul Luse 	printf("\t-m <blobid> filename - import contents of a file to a blob\n");
134c5e63c71SPaul Luse 	printf("\t-n <# clusters> - create new blob\n");
135c5e63c71SPaul Luse 	printf("\t-p <blobid> - set the superblob to the ID provided\n");
136c5e63c71SPaul Luse 	printf("\t-r <blobid> name - remove xattr name/value pair\n");
137ff4fd58bSMike Gerdts 	printf("\t-R - recover the blobstore: like fsck for the blobstore\n");
138c5e63c71SPaul Luse 	printf("\t-s <blobid> | bs - show blob info or blobstore info\n");
139c5e63c71SPaul Luse 	printf("\t-S - enter interactive shell mode\n");
140e5044f79SPaul Luse 	printf("\t-T <filename> - automated script mode\n");
1413453391bSMike Gerdts 	printf("\t-w <blobid> - delete (whack) a blob\n");
142d14c3bfbSMike Gerdts 	printf("\t-x <blobid> name value - set xattr name/value pair\n");
143d14c3bfbSMike Gerdts 	printf("\t-X - exit when in interactive shell mode\n");
144c5e63c71SPaul Luse 	printf("\n");
145c5e63c71SPaul Luse }
146c5e63c71SPaul Luse 
147c5e63c71SPaul Luse /*
1488fbbfccdSPaul Luse  * Prints usage and relevant error message.
1498fbbfccdSPaul Luse  */
1508fbbfccdSPaul Luse static void
151e5044f79SPaul Luse usage(struct cli_context_t *cli_context, char *msg)
1528fbbfccdSPaul Luse {
1538fbbfccdSPaul Luse 	if (msg) {
1548fbbfccdSPaul Luse 		printf("%s", msg);
1558fbbfccdSPaul Luse 	}
156e5044f79SPaul Luse 
1577fc14171SDaniel Verkamp 	if (!cli_context || cli_context->cli_mode == CLI_MODE_CMD) {
1588fbbfccdSPaul Luse 		printf("Version %s\n", SPDK_VERSION_STRING);
1596a41e84cSWANGHAILIANG 		printf("Usage: %s [-j SPDK josn_config_file] Command\n", program_name);
1608fbbfccdSPaul Luse 		printf("\n%s is a command line tool for interacting with blobstore\n",
1618fbbfccdSPaul Luse 		       program_name);
1628fbbfccdSPaul Luse 		printf("on the underlying device specified in the conf file passed\n");
1638fbbfccdSPaul Luse 		printf("in as a command line option.\n");
164e5044f79SPaul Luse 	}
1657fc14171SDaniel Verkamp 	if (!cli_context || cli_context->cli_mode != CLI_MODE_SCRIPT) {
166e5044f79SPaul Luse 		print_cmds();
167e5044f79SPaul Luse 	}
1688fbbfccdSPaul Luse }
1698fbbfccdSPaul Luse 
1708fbbfccdSPaul Luse /*
1718fbbfccdSPaul Luse  * Free up memory that we allocated.
1728fbbfccdSPaul Luse  */
1738fbbfccdSPaul Luse static void
1748fbbfccdSPaul Luse cli_cleanup(struct cli_context_t *cli_context)
1758fbbfccdSPaul Luse {
1768fbbfccdSPaul Luse 	if (cli_context->buff) {
17727a23a33SDarek Stojaczyk 		spdk_free(cli_context->buff);
1788fbbfccdSPaul Luse 	}
179e5044f79SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
180e5044f79SPaul Luse 		int i;
181e5044f79SPaul Luse 
182e5044f79SPaul Luse 		for (i = 0; i <= g_script.max_index; i++) {
183e5044f79SPaul Luse 			free(g_script.cmdline[i]);
184e5044f79SPaul Luse 		}
185e5044f79SPaul Luse 	}
1868fbbfccdSPaul Luse 	free(cli_context);
1878fbbfccdSPaul Luse }
1888fbbfccdSPaul Luse 
1898fbbfccdSPaul Luse /*
1908fbbfccdSPaul Luse  * Callback routine for the blobstore unload.
1918fbbfccdSPaul Luse  */
1928fbbfccdSPaul Luse static void
1938fbbfccdSPaul Luse unload_complete(void *cb_arg, int bserrno)
1948fbbfccdSPaul Luse {
1958fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
1968fbbfccdSPaul Luse 
1978fbbfccdSPaul Luse 	if (bserrno) {
1988fbbfccdSPaul Luse 		printf("Error %d unloading the bobstore\n", bserrno);
1998fbbfccdSPaul Luse 		cli_context->rc = bserrno;
2008fbbfccdSPaul Luse 	}
2018fbbfccdSPaul Luse 
202c5e63c71SPaul Luse 	/*
203c5e63c71SPaul Luse 	 * Quit if we're in cmd mode or exiting shell mode, otherwise
204c5e63c71SPaul Luse 	 * clear the action field and start the main function again.
205c5e63c71SPaul Luse 	 */
206c5e63c71SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_CMD ||
207c5e63c71SPaul Luse 	    cli_context->action == CLI_SHELL_EXIT) {
2088fbbfccdSPaul Luse 		spdk_app_stop(cli_context->rc);
209c5e63c71SPaul Luse 	} else {
210d4e565b4SPaul Luse 		/* when action is CLI_NONE, we know we need to remain in the shell */
211d4e565b4SPaul Luse 		cli_context->bs = NULL;
212c5e63c71SPaul Luse 		cli_context->action = CLI_NONE;
213deb8ee5cSBen Walker 		cli_start(cli_context);
214c5e63c71SPaul Luse 	}
2158fbbfccdSPaul Luse }
2168fbbfccdSPaul Luse 
2178fbbfccdSPaul Luse /*
2188fbbfccdSPaul Luse  * Unload the blobstore.
2198fbbfccdSPaul Luse  */
2208fbbfccdSPaul Luse static void
2218fbbfccdSPaul Luse unload_bs(struct cli_context_t *cli_context, char *msg, int bserrno)
2228fbbfccdSPaul Luse {
2238fbbfccdSPaul Luse 	if (bserrno) {
2248fbbfccdSPaul Luse 		printf("%s (err %d)\n", msg, bserrno);
2258fbbfccdSPaul Luse 		cli_context->rc = bserrno;
2268fbbfccdSPaul Luse 	}
227c5e63c71SPaul Luse 
2288fbbfccdSPaul Luse 	if (cli_context->bs) {
229c5e63c71SPaul Luse 		if (cli_context->channel) {
230c5e63c71SPaul Luse 			spdk_bs_free_io_channel(cli_context->channel);
231d4e565b4SPaul Luse 			cli_context->channel = NULL;
232c5e63c71SPaul Luse 		}
2338fbbfccdSPaul Luse 		spdk_bs_unload(cli_context->bs, unload_complete, cli_context);
234a23fa64cSPaul Luse 	} else if (cli_context->cli_mode != CLI_MODE_SCRIPT) {
2358fbbfccdSPaul Luse 		spdk_app_stop(bserrno);
236a23fa64cSPaul Luse 
2378fbbfccdSPaul Luse 	}
2388fbbfccdSPaul Luse }
2398fbbfccdSPaul Luse 
2408fbbfccdSPaul Luse /*
2418fbbfccdSPaul Luse  * Callback for closing a blob.
2428fbbfccdSPaul Luse  */
2438fbbfccdSPaul Luse static void
2448fbbfccdSPaul Luse close_cb(void *arg1, int bserrno)
2458fbbfccdSPaul Luse {
2468fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
2478fbbfccdSPaul Luse 
2488fbbfccdSPaul Luse 	if (bserrno) {
2498fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in close callback",
2508fbbfccdSPaul Luse 			  bserrno);
2518fbbfccdSPaul Luse 		return;
2528fbbfccdSPaul Luse 	}
2538fbbfccdSPaul Luse 	unload_bs(cli_context, "", 0);
2548fbbfccdSPaul Luse }
2558fbbfccdSPaul Luse 
2568fbbfccdSPaul Luse /*
257b7876f9aSJosh Soref  * Callback function for syncing metadata.
2588fbbfccdSPaul Luse  */
2598fbbfccdSPaul Luse static void
260866f15b3SPaul Luse sync_cb(void *arg1, int bserrno)
2618fbbfccdSPaul Luse {
2628fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
2638fbbfccdSPaul Luse 
2648fbbfccdSPaul Luse 	if (bserrno) {
2658fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in sync callback",
2668fbbfccdSPaul Luse 			  bserrno);
2678fbbfccdSPaul Luse 		return;
2688fbbfccdSPaul Luse 	}
2698fbbfccdSPaul Luse 
270e734bb9fSJim Harris 	spdk_blob_close(cli_context->blob, close_cb, cli_context);
2718fbbfccdSPaul Luse }
2728fbbfccdSPaul Luse 
2738fbbfccdSPaul Luse static void
274463925ffSJim Harris resize_cb(void *cb_arg, int bserrno)
2758fbbfccdSPaul Luse {
2768fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
2778fbbfccdSPaul Luse 	uint64_t total = 0;
2788fbbfccdSPaul Luse 
2798fbbfccdSPaul Luse 	if (bserrno) {
2808fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in blob resize",
2818fbbfccdSPaul Luse 			  bserrno);
2828fbbfccdSPaul Luse 		return;
2838fbbfccdSPaul Luse 	}
2848fbbfccdSPaul Luse 
2858fbbfccdSPaul Luse 	total = spdk_blob_get_num_clusters(cli_context->blob);
2868fbbfccdSPaul Luse 	printf("blob now has USED clusters of %" PRIu64 "\n",
2878fbbfccdSPaul Luse 	       total);
2888fbbfccdSPaul Luse 
2898fbbfccdSPaul Luse 	/*
2908fbbfccdSPaul Luse 	 * Always a good idea to sync after MD changes or the changes
2918fbbfccdSPaul Luse 	 * may be lost if things aren't closed cleanly.
2928fbbfccdSPaul Luse 	 */
2932c3591f1SJim Harris 	spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context);
2948fbbfccdSPaul Luse }
2958fbbfccdSPaul Luse 
2968fbbfccdSPaul Luse /*
297463925ffSJim Harris  * Callback function for opening a blob after creating.
298463925ffSJim Harris  */
299463925ffSJim Harris static void
300463925ffSJim Harris open_now_resize_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
301463925ffSJim Harris {
302463925ffSJim Harris 	struct cli_context_t *cli_context = cb_arg;
303463925ffSJim Harris 
304463925ffSJim Harris 	if (bserrno) {
305463925ffSJim Harris 		unload_bs(cli_context, "Error in open completion",
306463925ffSJim Harris 			  bserrno);
307463925ffSJim Harris 		return;
308463925ffSJim Harris 	}
309463925ffSJim Harris 	cli_context->blob = blob;
310463925ffSJim Harris 
311463925ffSJim Harris 	spdk_blob_resize(cli_context->blob, cli_context->num_clusters,
312463925ffSJim Harris 			 resize_cb, cli_context);
313463925ffSJim Harris }
314463925ffSJim Harris 
315463925ffSJim Harris /*
3168fbbfccdSPaul Luse  * Callback function for creating a blob.
3178fbbfccdSPaul Luse  */
3188fbbfccdSPaul Luse static void
319866f15b3SPaul Luse blob_create_cb(void *arg1, spdk_blob_id blobid, int bserrno)
3208fbbfccdSPaul Luse {
3218fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
3228fbbfccdSPaul Luse 
3238fbbfccdSPaul Luse 	if (bserrno) {
3248fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in blob create callback",
3258fbbfccdSPaul Luse 			  bserrno);
3268fbbfccdSPaul Luse 		return;
3278fbbfccdSPaul Luse 	}
3288fbbfccdSPaul Luse 
3298fbbfccdSPaul Luse 	cli_context->blobid = blobid;
33076a577b0SMike Gerdts 	printf("New blob id 0x%" PRIx64 "\n", cli_context->blobid);
3318fbbfccdSPaul Luse 
332e5044f79SPaul Luse 	/* if we're in script mode, we need info on all blobids for later */
333e5044f79SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
334e5044f79SPaul Luse 		g_script.blobid[g_script.blobid_idx++] = blobid;
335e5044f79SPaul Luse 	}
336e5044f79SPaul Luse 
3378fbbfccdSPaul Luse 	/* We have to open the blob before we can do things like resize. */
338d52dbda2SJim Harris 	spdk_bs_open_blob(cli_context->bs, cli_context->blobid,
339866f15b3SPaul Luse 			  open_now_resize_cb, cli_context);
3408fbbfccdSPaul Luse }
3418fbbfccdSPaul Luse 
3428fbbfccdSPaul Luse /*
3438fbbfccdSPaul Luse  * Callback for get_super where we'll continue on to show blobstore info.
3448fbbfccdSPaul Luse  */
3458fbbfccdSPaul Luse static void
346866f15b3SPaul Luse show_bs_cb(void *arg1, spdk_blob_id blobid, int bserrno)
3478fbbfccdSPaul Luse {
3488fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
349b3781b22SDaniel Verkamp 	struct spdk_bs_type bstype;
3508fbbfccdSPaul Luse 	uint64_t val;
3518fbbfccdSPaul Luse 	struct spdk_bdev *bdev = NULL;
3528fbbfccdSPaul Luse 
3538fbbfccdSPaul Luse 	if (bserrno && bserrno != -ENOENT) {
3548fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in get_super callback",
3558fbbfccdSPaul Luse 			  bserrno);
3568fbbfccdSPaul Luse 		return;
3578fbbfccdSPaul Luse 	}
3588fbbfccdSPaul Luse 	cli_context->superid = blobid;
3598fbbfccdSPaul Luse 
3608fbbfccdSPaul Luse 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
3618fbbfccdSPaul Luse 	if (bdev == NULL) {
3628fbbfccdSPaul Luse 		unload_bs(cli_context, "Error w/bdev in get_super callback",
3638fbbfccdSPaul Luse 			  bserrno);
3648fbbfccdSPaul Luse 		return;
3658fbbfccdSPaul Luse 	}
3668fbbfccdSPaul Luse 
3678fbbfccdSPaul Luse 	printf("Blobstore Public Info:\n");
3688fbbfccdSPaul Luse 	printf("\tUsing bdev Product Name: %s\n",
3698fbbfccdSPaul Luse 	       spdk_bdev_get_product_name(bdev));
3708fbbfccdSPaul Luse 	printf("\tAPI Version: %d\n", SPDK_BS_VERSION);
3718fbbfccdSPaul Luse 
3728fbbfccdSPaul Luse 	if (bserrno != -ENOENT) {
37376a577b0SMike Gerdts 		printf("\tsuper blob ID: 0x%" PRIx64 "\n", cli_context->superid);
3748fbbfccdSPaul Luse 	} else {
3758fbbfccdSPaul Luse 		printf("\tsuper blob ID: none assigned\n");
3768fbbfccdSPaul Luse 	}
3778fbbfccdSPaul Luse 
3783299bf6dSJim Harris 	printf("\tmd page size: %" PRIu64 "\n", cli_context->page_size);
3796751b44bSTomasz Zawadzki 	printf("\tio unit size: %" PRIu64 "\n", cli_context->io_unit_size);
3808fbbfccdSPaul Luse 
3818fbbfccdSPaul Luse 	val = spdk_bs_get_cluster_size(cli_context->bs);
3828fbbfccdSPaul Luse 	printf("\tcluster size: %" PRIu64 "\n", val);
3838fbbfccdSPaul Luse 
3848fbbfccdSPaul Luse 	val = spdk_bs_free_cluster_count(cli_context->bs);
3858fbbfccdSPaul Luse 	printf("\t# free clusters: %" PRIu64 "\n", val);
3868fbbfccdSPaul Luse 
387b3781b22SDaniel Verkamp 	bstype = spdk_bs_get_bstype(cli_context->bs);
388b4b7d5d3SJim Harris 	spdk_log_dump(stdout, "\tblobstore type:", &bstype, sizeof(bstype));
389b3781b22SDaniel Verkamp 
3908fbbfccdSPaul Luse 	/*
3918fbbfccdSPaul Luse 	 * Private info isn't accessible via the public API but
3928fbbfccdSPaul Luse 	 * may be useful for debug of blobstore based applications.
3938fbbfccdSPaul Luse 	 */
3948fbbfccdSPaul Luse 	printf("\nBlobstore Private Info:\n");
3958fbbfccdSPaul Luse 	printf("\tMetadata start (pages): %" PRIu64 "\n",
3968fbbfccdSPaul Luse 	       cli_context->bs->md_start);
3978fbbfccdSPaul Luse 	printf("\tMetadata length (pages): %d\n",
3988fbbfccdSPaul Luse 	       cli_context->bs->md_len);
3998fbbfccdSPaul Luse 
4008fbbfccdSPaul Luse 	unload_bs(cli_context, "", 0);
4018fbbfccdSPaul Luse }
4028fbbfccdSPaul Luse 
4038fbbfccdSPaul Luse /*
4048fbbfccdSPaul Luse  * Show detailed info about a particular blob.
4058fbbfccdSPaul Luse  */
4068fbbfccdSPaul Luse static void
4078fbbfccdSPaul Luse show_blob(struct cli_context_t *cli_context)
4088fbbfccdSPaul Luse {
4098fbbfccdSPaul Luse 	uint64_t val;
4108fbbfccdSPaul Luse 	struct spdk_xattr_names *names;
4118fbbfccdSPaul Luse 	const void *value;
4128fbbfccdSPaul Luse 	size_t value_len;
4138fbbfccdSPaul Luse 	unsigned int i;
4148fbbfccdSPaul Luse 
4158fbbfccdSPaul Luse 	printf("Blob Public Info:\n");
4168fbbfccdSPaul Luse 
41776a577b0SMike Gerdts 	printf("blob ID: 0x%" PRIx64 "\n", cli_context->blobid);
4188fbbfccdSPaul Luse 
4198fbbfccdSPaul Luse 	val = spdk_blob_get_num_clusters(cli_context->blob);
4208fbbfccdSPaul Luse 	printf("# of clusters: %" PRIu64 "\n", val);
4218fbbfccdSPaul Luse 
4228fbbfccdSPaul Luse 	printf("# of bytes: %" PRIu64 "\n",
4238fbbfccdSPaul Luse 	       val * spdk_bs_get_cluster_size(cli_context->bs));
4248fbbfccdSPaul Luse 
4253299bf6dSJim Harris 	val = spdk_blob_get_num_io_units(cli_context->blob);
4263299bf6dSJim Harris 	printf("# of io units: %" PRIu64 "\n", val);
4278fbbfccdSPaul Luse 
4282c3591f1SJim Harris 	spdk_blob_get_xattr_names(cli_context->blob, &names);
4298fbbfccdSPaul Luse 
4308fbbfccdSPaul Luse 	printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names));
4318fbbfccdSPaul Luse 	printf("xattrs:\n");
4328fbbfccdSPaul Luse 	for (i = 0; i < spdk_xattr_names_get_count(names); i++) {
4332c3591f1SJim Harris 		spdk_blob_get_xattr_value(cli_context->blob,
4348fbbfccdSPaul Luse 					  spdk_xattr_names_get_name(names, i),
4358fbbfccdSPaul Luse 					  &value, &value_len);
43613d6d003SRichael Zhuang 		if (value_len > BUFSIZE) {
4378fbbfccdSPaul Luse 			printf("FYI: adjusting size of xattr due to CLI limits.\n");
43813d6d003SRichael Zhuang 			value_len = BUFSIZE + 1;
4398fbbfccdSPaul Luse 		}
4408fbbfccdSPaul Luse 		printf("\n(%d) Name:%s\n", i,
4418fbbfccdSPaul Luse 		       spdk_xattr_names_get_name(names, i));
4428fbbfccdSPaul Luse 		printf("(%d) Value:\n", i);
44313d6d003SRichael Zhuang 		spdk_log_dump(stdout, "", value, value_len - 1);
4448fbbfccdSPaul Luse 	}
4458fbbfccdSPaul Luse 
4468fbbfccdSPaul Luse 	/*
4478fbbfccdSPaul Luse 	 * Private info isn't accessible via the public API but
4488fbbfccdSPaul Luse 	 * may be useful for debug of blobstore based applications.
4498fbbfccdSPaul Luse 	 */
4508fbbfccdSPaul Luse 	printf("\nBlob Private Info:\n");
451c8efd8a8SJim Harris 	switch (cli_context->blob->state) {
4528fbbfccdSPaul Luse 	case SPDK_BLOB_STATE_DIRTY:
4538fbbfccdSPaul Luse 		printf("state: DIRTY\n");
4548fbbfccdSPaul Luse 		break;
4558fbbfccdSPaul Luse 	case SPDK_BLOB_STATE_CLEAN:
4568fbbfccdSPaul Luse 		printf("state: CLEAN\n");
4578fbbfccdSPaul Luse 		break;
4588fbbfccdSPaul Luse 	case SPDK_BLOB_STATE_LOADING:
4598fbbfccdSPaul Luse 		printf("state: LOADING\n");
4608fbbfccdSPaul Luse 		break;
4618fbbfccdSPaul Luse 	default:
4628fbbfccdSPaul Luse 		printf("state: UNKNOWN\n");
4638fbbfccdSPaul Luse 		break;
4648fbbfccdSPaul Luse 	}
4658fbbfccdSPaul Luse 	printf("open ref count: %d\n",
466c8efd8a8SJim Harris 	       cli_context->blob->open_ref);
467c5e63c71SPaul Luse 
468c5e63c71SPaul Luse 	spdk_xattr_names_free(names);
4698fbbfccdSPaul Luse }
4708fbbfccdSPaul Luse 
4718fbbfccdSPaul Luse /*
472866f15b3SPaul Luse  * Callback for getting the first blob, shared with simple blob listing as well.
4738fbbfccdSPaul Luse  */
4748fbbfccdSPaul Luse static void
4758fbbfccdSPaul Luse blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno)
4768fbbfccdSPaul Luse {
4778fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
4788fbbfccdSPaul Luse 
4798fbbfccdSPaul Luse 	if (bserrno) {
4808fbbfccdSPaul Luse 		if (bserrno == -ENOENT) {
4818fbbfccdSPaul Luse 			/* this simply means there are no more blobs */
4828fbbfccdSPaul Luse 			unload_bs(cli_context, "", 0);
4838fbbfccdSPaul Luse 		} else {
4848fbbfccdSPaul Luse 			unload_bs(cli_context, "Error in blob iter callback",
4858fbbfccdSPaul Luse 				  bserrno);
4868fbbfccdSPaul Luse 		}
4878fbbfccdSPaul Luse 		return;
4888fbbfccdSPaul Luse 	}
4898fbbfccdSPaul Luse 
4908fbbfccdSPaul Luse 	if (cli_context->action == CLI_LIST_BLOBS) {
491866f15b3SPaul Luse 		printf("\nList BLOBS:\n");
49276a577b0SMike Gerdts 		printf("Found blob with ID# 0x%" PRIx64 "\n",
4938fbbfccdSPaul Luse 		       spdk_blob_get_id(blob));
4948fbbfccdSPaul Luse 	} else if (spdk_blob_get_id(blob) == cli_context->blobid) {
4958fbbfccdSPaul Luse 		/*
4968fbbfccdSPaul Luse 		 * Found the blob we're looking for, but we need to finish
4978fbbfccdSPaul Luse 		 * iterating even after showing the info so that internally
4988fbbfccdSPaul Luse 		 * the blobstore logic will close the blob. Or we could
4998fbbfccdSPaul Luse 		 * chose to close it now, either way.
5008fbbfccdSPaul Luse 		 */
5018fbbfccdSPaul Luse 		cli_context->blob = blob;
5028fbbfccdSPaul Luse 		show_blob(cli_context);
5038fbbfccdSPaul Luse 	}
5048fbbfccdSPaul Luse 
505ae5a01ddSJim Harris 	spdk_bs_iter_next(cli_context->bs, blob, blob_iter_cb, cli_context);
5068fbbfccdSPaul Luse }
5078fbbfccdSPaul Luse 
5088fbbfccdSPaul Luse /*
5098fbbfccdSPaul Luse  * Callback for setting the super blob ID.
5108fbbfccdSPaul Luse  */
5118fbbfccdSPaul Luse static void
5128fbbfccdSPaul Luse set_super_cb(void *arg1, int bserrno)
5138fbbfccdSPaul Luse {
5148fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
5158fbbfccdSPaul Luse 
5168fbbfccdSPaul Luse 	if (bserrno) {
5178fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in set_super callback",
5188fbbfccdSPaul Luse 			  bserrno);
5198fbbfccdSPaul Luse 		return;
5208fbbfccdSPaul Luse 	}
5218fbbfccdSPaul Luse 
5228fbbfccdSPaul Luse 	printf("Super Blob ID has been set.\n");
5238fbbfccdSPaul Luse 	unload_bs(cli_context, "", 0);
5248fbbfccdSPaul Luse }
5258fbbfccdSPaul Luse 
5268fbbfccdSPaul Luse /*
5278fbbfccdSPaul Luse  * Callback for set_xattr_open where we set or delete xattrs.
5288fbbfccdSPaul Luse  */
5298fbbfccdSPaul Luse static void
530866f15b3SPaul Luse set_xattr_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
5318fbbfccdSPaul Luse {
5328fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
5338fbbfccdSPaul Luse 
5348fbbfccdSPaul Luse 	if (bserrno) {
5358fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in blob open callback",
5368fbbfccdSPaul Luse 			  bserrno);
5378fbbfccdSPaul Luse 		return;
5388fbbfccdSPaul Luse 	}
5398fbbfccdSPaul Luse 	cli_context->blob = blob;
5408fbbfccdSPaul Luse 
5418fbbfccdSPaul Luse 	if (cli_context->action == CLI_SET_XATTR) {
5422c3591f1SJim Harris 		spdk_blob_set_xattr(cli_context->blob, cli_context->key,
5432c3591f1SJim Harris 				    cli_context->value, strlen(cli_context->value) + 1);
5448fbbfccdSPaul Luse 		printf("Xattr has been set.\n");
5458fbbfccdSPaul Luse 	} else {
5462c3591f1SJim Harris 		spdk_blob_remove_xattr(cli_context->blob, cli_context->key);
5478fbbfccdSPaul Luse 		printf("Xattr has been removed.\n");
5488fbbfccdSPaul Luse 	}
5498fbbfccdSPaul Luse 
5502c3591f1SJim Harris 	spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context);
5518fbbfccdSPaul Luse }
5528fbbfccdSPaul Luse 
553f45c98ddSJim Harris static void __read_dump_cb(void *arg1);
554f45c98ddSJim Harris 
5558fbbfccdSPaul Luse /*
5568fbbfccdSPaul Luse  * Callback function for reading a blob for dumping to a file.
5578fbbfccdSPaul Luse  */
5588fbbfccdSPaul Luse static void
559866f15b3SPaul Luse read_dump_cb(void *arg1, int bserrno)
5608fbbfccdSPaul Luse {
5618fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
5628fbbfccdSPaul Luse 	uint64_t bytes_written;
5638fbbfccdSPaul Luse 
5648fbbfccdSPaul Luse 	if (bserrno) {
5658fbbfccdSPaul Luse 		fclose(cli_context->fp);
5668fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in read completion",
5678fbbfccdSPaul Luse 			  bserrno);
5688fbbfccdSPaul Luse 		return;
5698fbbfccdSPaul Luse 	}
5708fbbfccdSPaul Luse 
5716751b44bSTomasz Zawadzki 	bytes_written = fwrite(cli_context->buff, NUM_IO_UNITS, cli_context->io_unit_size,
5728fbbfccdSPaul Luse 			       cli_context->fp);
5736751b44bSTomasz Zawadzki 	if (bytes_written != cli_context->io_unit_size) {
5748fbbfccdSPaul Luse 		fclose(cli_context->fp);
5758fbbfccdSPaul Luse 		unload_bs(cli_context, "Error with fwrite",
5768fbbfccdSPaul Luse 			  bserrno);
5778fbbfccdSPaul Luse 		return;
5788fbbfccdSPaul Luse 	}
5798fbbfccdSPaul Luse 
580f45c98ddSJim Harris 	/* This completion may have occurred in the context of a read to
581f45c98ddSJim Harris 	 * an unallocated cluster.  So we can't issue the next read here, or
582f45c98ddSJim Harris 	 * we risk overflowing the stack.  So use spdk_thread_send_msg() to
583f45c98ddSJim Harris 	 * make sure we unwind before doing the next read.
584f45c98ddSJim Harris 	 */
585f45c98ddSJim Harris 	spdk_thread_send_msg(spdk_get_thread(), __read_dump_cb, cli_context);
586f45c98ddSJim Harris }
587f45c98ddSJim Harris 
588f45c98ddSJim Harris static void
589f45c98ddSJim Harris __read_dump_cb(void *arg1)
590f45c98ddSJim Harris {
591f45c98ddSJim Harris 	struct cli_context_t *cli_context = arg1;
592f45c98ddSJim Harris 
5938fbbfccdSPaul Luse 	printf(".");
5946751b44bSTomasz Zawadzki 	if (++cli_context->io_unit_count < cli_context->blob_io_units) {
5958fbbfccdSPaul Luse 		/* perform another read */
59666fc591fSJim Harris 		spdk_blob_io_read(cli_context->blob, cli_context->channel,
5976751b44bSTomasz Zawadzki 				  cli_context->buff, cli_context->io_unit_count,
5986751b44bSTomasz Zawadzki 				  NUM_IO_UNITS, read_dump_cb, cli_context);
5998fbbfccdSPaul Luse 	} else {
6008fbbfccdSPaul Luse 		/* done reading */
601e9a50eb8SPaul Luse 		printf("\nFile write complete (to %s).\n", cli_context->file);
6028fbbfccdSPaul Luse 		fclose(cli_context->fp);
603e734bb9fSJim Harris 		spdk_blob_close(cli_context->blob, close_cb, cli_context);
6048fbbfccdSPaul Luse 	}
6058fbbfccdSPaul Luse }
6068fbbfccdSPaul Luse 
6078fbbfccdSPaul Luse /*
6088fbbfccdSPaul Luse  * Callback for write completion on the import of a file to a blob.
6098fbbfccdSPaul Luse  */
6108fbbfccdSPaul Luse static void
611866f15b3SPaul Luse write_imp_cb(void *arg1, int bserrno)
6128fbbfccdSPaul Luse {
6138fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
6148fbbfccdSPaul Luse 	uint64_t bytes_read;
6158fbbfccdSPaul Luse 
6168fbbfccdSPaul Luse 	if (bserrno) {
6178fbbfccdSPaul Luse 		fclose(cli_context->fp);
6188fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in write completion",
6198fbbfccdSPaul Luse 			  bserrno);
6208fbbfccdSPaul Luse 		return;
6218fbbfccdSPaul Luse 	}
6228fbbfccdSPaul Luse 
6238fbbfccdSPaul Luse 	if (cli_context->bytes_so_far < cli_context->filesize) {
6248fbbfccdSPaul Luse 		/* perform another file read */
6258fbbfccdSPaul Luse 		bytes_read = fread(cli_context->buff, 1,
6266751b44bSTomasz Zawadzki 				   cli_context->io_unit_size,
6278fbbfccdSPaul Luse 				   cli_context->fp);
6288fbbfccdSPaul Luse 		cli_context->bytes_so_far += bytes_read;
6298fbbfccdSPaul Luse 
6306751b44bSTomasz Zawadzki 		/* if this read is < 1 io_unit, fill with 0s */
6316751b44bSTomasz Zawadzki 		if (bytes_read < cli_context->io_unit_size) {
6328fbbfccdSPaul Luse 			uint8_t *offset = cli_context->buff + bytes_read;
6336751b44bSTomasz Zawadzki 			memset(offset, 0, cli_context->io_unit_size - bytes_read);
6348fbbfccdSPaul Luse 		}
6358fbbfccdSPaul Luse 	} else {
6368fbbfccdSPaul Luse 		/*
6378fbbfccdSPaul Luse 		 * Done reading the file, fill the rest of the blob with 0s,
6386751b44bSTomasz Zawadzki 		 * yeah we're memsetting the same io_unit over and over here
6398fbbfccdSPaul Luse 		 */
6406751b44bSTomasz Zawadzki 		memset(cli_context->buff, 0, cli_context->io_unit_size);
6418fbbfccdSPaul Luse 	}
6426751b44bSTomasz Zawadzki 	if (++cli_context->io_unit_count < cli_context->blob_io_units) {
6438fbbfccdSPaul Luse 		printf(".");
64466fc591fSJim Harris 		spdk_blob_io_write(cli_context->blob, cli_context->channel,
6456751b44bSTomasz Zawadzki 				   cli_context->buff, cli_context->io_unit_count,
6466751b44bSTomasz Zawadzki 				   NUM_IO_UNITS, write_imp_cb, cli_context);
6478fbbfccdSPaul Luse 	} else {
6488fbbfccdSPaul Luse 		/* done writing */
649e9a50eb8SPaul Luse 		printf("\nBlob import complete (from %s).\n", cli_context->file);
6508fbbfccdSPaul Luse 		fclose(cli_context->fp);
651e734bb9fSJim Harris 		spdk_blob_close(cli_context->blob, close_cb, cli_context);
6528fbbfccdSPaul Luse 	}
6538fbbfccdSPaul Luse }
6548fbbfccdSPaul Luse 
6558fbbfccdSPaul Luse /*
6568fbbfccdSPaul Luse  * Callback for open blobs where we'll continue on dump a blob to a file or
6578fbbfccdSPaul Luse  * import a file to a blob. For dump, the resulting file will always be the
6588fbbfccdSPaul Luse  * full size of the blob.  For import, the blob will fill with the file
6598fbbfccdSPaul Luse  * contents first and then 0 out the rest of the blob.
6608fbbfccdSPaul Luse  */
6618fbbfccdSPaul Luse static void
662866f15b3SPaul Luse dump_imp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
6638fbbfccdSPaul Luse {
6648fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
6658fbbfccdSPaul Luse 
6668fbbfccdSPaul Luse 	if (bserrno) {
6678fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in blob open callback",
6688fbbfccdSPaul Luse 			  bserrno);
6698fbbfccdSPaul Luse 		return;
6708fbbfccdSPaul Luse 	}
6718fbbfccdSPaul Luse 	cli_context->blob = blob;
6728fbbfccdSPaul Luse 
6738fbbfccdSPaul Luse 	/*
6746751b44bSTomasz Zawadzki 	 * We'll transfer just one io_unit at a time to keep the buffer
6758fbbfccdSPaul Luse 	 * small. This could be bigger of course.
6768fbbfccdSPaul Luse 	 */
67727a23a33SDarek Stojaczyk 	cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL,
67827a23a33SDarek Stojaczyk 					SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
6798fbbfccdSPaul Luse 	if (cli_context->buff == NULL) {
680809dc418SPaul Luse 		printf("Error in allocating memory\n");
681e734bb9fSJim Harris 		spdk_blob_close(cli_context->blob, close_cb, cli_context);
6828fbbfccdSPaul Luse 		return;
6838fbbfccdSPaul Luse 	}
6848fbbfccdSPaul Luse 	printf("Working");
6856751b44bSTomasz Zawadzki 	cli_context->blob_io_units = spdk_blob_get_num_io_units(cli_context->blob);
6866751b44bSTomasz Zawadzki 	cli_context->io_unit_count = 0;
6874952f254SDaniel Verkamp 	if (cli_context->action == CLI_DUMP_BLOB) {
6888fbbfccdSPaul Luse 		cli_context->fp = fopen(cli_context->file, "w");
689809dc418SPaul Luse 		if (cli_context->fp == NULL) {
690809dc418SPaul Luse 			printf("Error in opening file\n");
691e734bb9fSJim Harris 			spdk_blob_close(cli_context->blob, close_cb, cli_context);
692809dc418SPaul Luse 			return;
693809dc418SPaul Luse 		}
6948fbbfccdSPaul Luse 
6958389b65aSMike Gerdts 		if (cli_context->blob->active.num_clusters == 0) {
6968389b65aSMike Gerdts 			fclose(cli_context->fp);
6978389b65aSMike Gerdts 			spdk_blob_close(cli_context->blob, close_cb, cli_context);
6988389b65aSMike Gerdts 			return;
6998389b65aSMike Gerdts 		}
7008389b65aSMike Gerdts 
7016751b44bSTomasz Zawadzki 		/* read a io_unit of data from the blob */
70266fc591fSJim Harris 		spdk_blob_io_read(cli_context->blob, cli_context->channel,
7036751b44bSTomasz Zawadzki 				  cli_context->buff, cli_context->io_unit_count,
7046751b44bSTomasz Zawadzki 				  NUM_IO_UNITS, read_dump_cb, cli_context);
7058fbbfccdSPaul Luse 	} else {
7068fbbfccdSPaul Luse 		cli_context->fp = fopen(cli_context->file, "r");
707809dc418SPaul Luse 		if (cli_context->fp == NULL) {
708a0e22e13Spaul luse 			printf("Error in opening file: errno %d\n", errno);
709e734bb9fSJim Harris 			spdk_blob_close(cli_context->blob, close_cb, cli_context);
710809dc418SPaul Luse 			return;
711809dc418SPaul Luse 		}
7128fbbfccdSPaul Luse 
7136751b44bSTomasz Zawadzki 		/* get the filesize then rewind read a io_unit of data from file */
7148fbbfccdSPaul Luse 		fseek(cli_context->fp, 0L, SEEK_END);
7158fbbfccdSPaul Luse 		cli_context->filesize = ftell(cli_context->fp);
7168fbbfccdSPaul Luse 		rewind(cli_context->fp);
7176751b44bSTomasz Zawadzki 		cli_context->bytes_so_far = fread(cli_context->buff, NUM_IO_UNITS,
7186751b44bSTomasz Zawadzki 						  cli_context->io_unit_size,
7198fbbfccdSPaul Luse 						  cli_context->fp);
7208fbbfccdSPaul Luse 
7216751b44bSTomasz Zawadzki 		/* if the file is < a io_unit, fill the rest with 0s */
7226751b44bSTomasz Zawadzki 		if (cli_context->filesize < cli_context->io_unit_size) {
7238fbbfccdSPaul Luse 			uint8_t *offset =
7248fbbfccdSPaul Luse 				cli_context->buff + cli_context->filesize;
7258fbbfccdSPaul Luse 
7268fbbfccdSPaul Luse 			memset(offset, 0,
7276751b44bSTomasz Zawadzki 			       cli_context->io_unit_size - cli_context->filesize);
7288fbbfccdSPaul Luse 		}
7298fbbfccdSPaul Luse 
73066fc591fSJim Harris 		spdk_blob_io_write(cli_context->blob, cli_context->channel,
7316751b44bSTomasz Zawadzki 				   cli_context->buff, cli_context->io_unit_count,
7326751b44bSTomasz Zawadzki 				   NUM_IO_UNITS, write_imp_cb, cli_context);
7338fbbfccdSPaul Luse 	}
7348fbbfccdSPaul Luse }
7358fbbfccdSPaul Luse 
7368fbbfccdSPaul Luse /*
7376751b44bSTomasz Zawadzki  * Callback function for writing a specific pattern to io_unit 0.
7388fbbfccdSPaul Luse  */
7398fbbfccdSPaul Luse static void
740866f15b3SPaul Luse write_cb(void *arg1, int bserrno)
7418fbbfccdSPaul Luse {
7428fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
7438fbbfccdSPaul Luse 
7448fbbfccdSPaul Luse 	if (bserrno) {
7458fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in write completion",
7468fbbfccdSPaul Luse 			  bserrno);
7478fbbfccdSPaul Luse 		return;
7488fbbfccdSPaul Luse 	}
7498fbbfccdSPaul Luse 	printf(".");
7506751b44bSTomasz Zawadzki 	if (++cli_context->io_unit_count < cli_context->blob_io_units) {
75166fc591fSJim Harris 		spdk_blob_io_write(cli_context->blob, cli_context->channel,
7526751b44bSTomasz Zawadzki 				   cli_context->buff, cli_context->io_unit_count,
7536751b44bSTomasz Zawadzki 				   NUM_IO_UNITS, write_cb, cli_context);
7548fbbfccdSPaul Luse 	} else {
7558fbbfccdSPaul Luse 		/* done writing */
756e9a50eb8SPaul Luse 		printf("\nBlob fill complete (with 0x%x).\n", cli_context->fill_value);
757e734bb9fSJim Harris 		spdk_blob_close(cli_context->blob, close_cb, cli_context);
7588fbbfccdSPaul Luse 	}
7598fbbfccdSPaul Luse }
7608fbbfccdSPaul Luse 
7618fbbfccdSPaul Luse /*
762866f15b3SPaul Luse  * Callback function to fill a blob with a value, callback from open.
7638fbbfccdSPaul Luse  */
7648fbbfccdSPaul Luse static void
765866f15b3SPaul Luse fill_blob_cb(void *arg1, struct spdk_blob *blob, int bserrno)
7668fbbfccdSPaul Luse {
767866f15b3SPaul Luse 	struct cli_context_t *cli_context = arg1;
7688fbbfccdSPaul Luse 
7698fbbfccdSPaul Luse 	if (bserrno) {
770866f15b3SPaul Luse 		unload_bs(cli_context, "Error in open callback",
7718fbbfccdSPaul Luse 			  bserrno);
7728fbbfccdSPaul Luse 		return;
7738fbbfccdSPaul Luse 	}
774866f15b3SPaul Luse 
7758fbbfccdSPaul Luse 	cli_context->blob = blob;
7766751b44bSTomasz Zawadzki 	cli_context->io_unit_count = 0;
7776751b44bSTomasz Zawadzki 	cli_context->blob_io_units = spdk_blob_get_num_io_units(cli_context->blob);
77827a23a33SDarek Stojaczyk 	cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL,
77927a23a33SDarek Stojaczyk 					SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
7808fbbfccdSPaul Luse 	if (cli_context->buff == NULL) {
7818fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in allocating memory",
7828fbbfccdSPaul Luse 			  -ENOMEM);
7838fbbfccdSPaul Luse 		return;
7848fbbfccdSPaul Luse 	}
7858fbbfccdSPaul Luse 
7868fbbfccdSPaul Luse 	memset(cli_context->buff, cli_context->fill_value,
7876751b44bSTomasz Zawadzki 	       cli_context->io_unit_size);
788e5044f79SPaul Luse 	printf("Working");
78966fc591fSJim Harris 	spdk_blob_io_write(cli_context->blob, cli_context->channel,
7908fbbfccdSPaul Luse 			   cli_context->buff,
7916751b44bSTomasz Zawadzki 			   STARTING_IO_UNIT, NUM_IO_UNITS, write_cb, cli_context);
7928fbbfccdSPaul Luse }
7938fbbfccdSPaul Luse 
7948fbbfccdSPaul Luse /*
7953453391bSMike Gerdts  * Callback for deleting a blob
7963453391bSMike Gerdts  */
7973453391bSMike Gerdts static void
7983453391bSMike Gerdts delete_blob_cb(void *arg1, int bserrno)
7993453391bSMike Gerdts {
8003453391bSMike Gerdts 	struct cli_context_t *cli_context = arg1;
8013453391bSMike Gerdts 
8023453391bSMike Gerdts 	if (bserrno) {
8033453391bSMike Gerdts 		unload_bs(cli_context, "Error in delete_blob callback",
8043453391bSMike Gerdts 			  bserrno);
8053453391bSMike Gerdts 		return;
8063453391bSMike Gerdts 	}
8073453391bSMike Gerdts 
8083453391bSMike Gerdts 	printf("Blob 0x%lx has been deleted.\n", cli_context->blobid);
8093453391bSMike Gerdts 	unload_bs(cli_context, "", 0);
8103453391bSMike Gerdts }
8113453391bSMike Gerdts 
8123453391bSMike Gerdts /*
813866f15b3SPaul Luse  * Multiple actions require us to open the bs first so here we use
814866f15b3SPaul Luse  * a common callback to set a bunch of values and then move on to
815866f15b3SPaul Luse  * the next step saved off via function pointer.
8168fbbfccdSPaul Luse  */
8178fbbfccdSPaul Luse static void
818866f15b3SPaul Luse load_bs_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
8198fbbfccdSPaul Luse {
8208fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
8218fbbfccdSPaul Luse 
8228fbbfccdSPaul Luse 	if (bserrno) {
8238fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in load callback",
8248fbbfccdSPaul Luse 			  bserrno);
8258fbbfccdSPaul Luse 		return;
8268fbbfccdSPaul Luse 	}
827866f15b3SPaul Luse 
8288fbbfccdSPaul Luse 	cli_context->bs = bs;
8298fbbfccdSPaul Luse 	cli_context->page_size = spdk_bs_get_page_size(cli_context->bs);
8306751b44bSTomasz Zawadzki 	cli_context->io_unit_size = spdk_bs_get_io_unit_size(cli_context->bs);
8318fbbfccdSPaul Luse 	cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs);
8328fbbfccdSPaul Luse 	if (cli_context->channel == NULL) {
8338fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in allocating channel",
8348fbbfccdSPaul Luse 			  -ENOMEM);
8358fbbfccdSPaul Luse 		return;
8368fbbfccdSPaul Luse 	}
8378fbbfccdSPaul Luse 
838866f15b3SPaul Luse 	switch (cli_context->action) {
839866f15b3SPaul Luse 	case CLI_SET_SUPER:
840866f15b3SPaul Luse 		spdk_bs_set_super(cli_context->bs, cli_context->superid,
841866f15b3SPaul Luse 				  set_super_cb, cli_context);
842866f15b3SPaul Luse 		break;
843866f15b3SPaul Luse 	case CLI_SHOW_BS:
844866f15b3SPaul Luse 		spdk_bs_get_super(cli_context->bs, show_bs_cb, cli_context);
845866f15b3SPaul Luse 		break;
846866f15b3SPaul Luse 	case CLI_CREATE_BLOB:
847d52dbda2SJim Harris 		spdk_bs_create_blob(cli_context->bs, blob_create_cb, cli_context);
848866f15b3SPaul Luse 		break;
849866f15b3SPaul Luse 	case CLI_SET_XATTR:
850866f15b3SPaul Luse 	case CLI_REM_XATTR:
851d52dbda2SJim Harris 		spdk_bs_open_blob(cli_context->bs, cli_context->blobid,
852866f15b3SPaul Luse 				  set_xattr_cb, cli_context);
853866f15b3SPaul Luse 		break;
854866f15b3SPaul Luse 	case CLI_SHOW_BLOB:
855866f15b3SPaul Luse 	case CLI_LIST_BLOBS:
856d52dbda2SJim Harris 		spdk_bs_iter_first(cli_context->bs, blob_iter_cb, cli_context);
857866f15b3SPaul Luse 
858866f15b3SPaul Luse 		break;
8594952f254SDaniel Verkamp 	case CLI_DUMP_BLOB:
8604952f254SDaniel Verkamp 	case CLI_IMPORT_BLOB:
861d52dbda2SJim Harris 		spdk_bs_open_blob(cli_context->bs, cli_context->blobid,
862866f15b3SPaul Luse 				  dump_imp_open_cb, cli_context);
863866f15b3SPaul Luse 		break;
864866f15b3SPaul Luse 	case CLI_FILL:
865d52dbda2SJim Harris 		spdk_bs_open_blob(cli_context->bs, cli_context->blobid,
866866f15b3SPaul Luse 				  fill_blob_cb, cli_context);
867866f15b3SPaul Luse 		break;
868ff4fd58bSMike Gerdts 	case CLI_RECOVER:
869ff4fd58bSMike Gerdts 		unload_bs(cli_context, "", 0);
870ff4fd58bSMike Gerdts 		break;
8713453391bSMike Gerdts 	case CLI_DELETE_BLOB:
8723453391bSMike Gerdts 		spdk_bs_delete_blob(cli_context->bs, cli_context->blobid,
8733453391bSMike Gerdts 				    delete_blob_cb, cli_context);
8743453391bSMike Gerdts 		break;
875866f15b3SPaul Luse 
876866f15b3SPaul Luse 	default:
877866f15b3SPaul Luse 		/* should never get here */
87898a2ec3eSPaul Luse 		exit(-1);
879866f15b3SPaul Luse 		break;
880866f15b3SPaul Luse 	}
8818fbbfccdSPaul Luse }
8828fbbfccdSPaul Luse 
883c8658397SShuhei Matsumoto static void
884c8658397SShuhei Matsumoto base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
885c8658397SShuhei Matsumoto 		   void *event_ctx)
886c8658397SShuhei Matsumoto {
887ab2aad85SMike Gerdts 	printf("Unsupported bdev event: type %d on bdev %s\n", type, spdk_bdev_get_name(bdev));
888c8658397SShuhei Matsumoto }
889c8658397SShuhei Matsumoto 
8908fbbfccdSPaul Luse /*
891866f15b3SPaul Luse  * Load the blobstore.
8928fbbfccdSPaul Luse  */
8938fbbfccdSPaul Luse static void
8948fbbfccdSPaul Luse load_bs(struct cli_context_t *cli_context)
8958fbbfccdSPaul Luse {
8968fbbfccdSPaul Luse 	struct spdk_bs_dev *bs_dev = NULL;
897c8658397SShuhei Matsumoto 	int rc;
898ff4fd58bSMike Gerdts 	struct spdk_bs_opts	opts = {};
899ff4fd58bSMike Gerdts 	struct spdk_bs_opts	*optsp = NULL;
9008fbbfccdSPaul Luse 
901c8658397SShuhei Matsumoto 	rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb,
902c8658397SShuhei Matsumoto 					 NULL, &bs_dev);
903c8658397SShuhei Matsumoto 	if (rc != 0) {
904c8658397SShuhei Matsumoto 		printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc));
9058fbbfccdSPaul Luse 		spdk_app_stop(-1);
9068fbbfccdSPaul Luse 		return;
9078fbbfccdSPaul Luse 	}
9088fbbfccdSPaul Luse 
909ff4fd58bSMike Gerdts 	if (cli_context->action == CLI_RECOVER) {
910ff4fd58bSMike Gerdts 		spdk_bs_opts_init(&opts, sizeof(opts));
911ff4fd58bSMike Gerdts 		opts.force_recover = true;
912ff4fd58bSMike Gerdts 		optsp = &opts;
913ff4fd58bSMike Gerdts 	}
914ff4fd58bSMike Gerdts 
915ff4fd58bSMike Gerdts 	spdk_bs_load(bs_dev, optsp, load_bs_cb, cli_context);
9168fbbfccdSPaul Luse }
9178fbbfccdSPaul Luse 
9181906b075SShuhei Matsumoto static int
9191906b075SShuhei Matsumoto print_bdev(void *ctx, struct spdk_bdev *bdev)
9201906b075SShuhei Matsumoto {
9211906b075SShuhei Matsumoto 	uint32_t *count = ctx;
9221906b075SShuhei Matsumoto 
9231906b075SShuhei Matsumoto 	(*count)++;
9241906b075SShuhei Matsumoto 
9251906b075SShuhei Matsumoto 	printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev));
9261906b075SShuhei Matsumoto 	printf("\tbdev Product Name: %s\n",
9271906b075SShuhei Matsumoto 	       spdk_bdev_get_product_name(bdev));
9281906b075SShuhei Matsumoto 	return 0;
9291906b075SShuhei Matsumoto }
9301906b075SShuhei Matsumoto 
9318fbbfccdSPaul Luse /*
9328fbbfccdSPaul Luse  * Lists all the blobs on this blobstore.
9338fbbfccdSPaul Luse  */
9348fbbfccdSPaul Luse static void
935c5e63c71SPaul Luse list_bdevs(struct cli_context_t *cli_context)
9368fbbfccdSPaul Luse {
9371906b075SShuhei Matsumoto 	uint32_t count = 0;
9388fbbfccdSPaul Luse 
9398fbbfccdSPaul Luse 	printf("\nList bdevs:\n");
9408fbbfccdSPaul Luse 
9411906b075SShuhei Matsumoto 	spdk_for_each_bdev(&count, print_bdev);
9421906b075SShuhei Matsumoto 
9431906b075SShuhei Matsumoto 	if (count == 0) {
9448fbbfccdSPaul Luse 		printf("Could not find a bdev\n");
9458fbbfccdSPaul Luse 	}
9468fbbfccdSPaul Luse 
9478fbbfccdSPaul Luse 	printf("\n");
948c5e63c71SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_CMD) {
9498fbbfccdSPaul Luse 		spdk_app_stop(0);
950c5e63c71SPaul Luse 	} else {
951c5e63c71SPaul Luse 		cli_context->action = CLI_NONE;
952deb8ee5cSBen Walker 		cli_start(cli_context);
953c5e63c71SPaul Luse 	}
9548fbbfccdSPaul Luse }
9558fbbfccdSPaul Luse 
9568fbbfccdSPaul Luse /*
9578fbbfccdSPaul Luse  * Callback function for initializing a blob.
9588fbbfccdSPaul Luse  */
9598fbbfccdSPaul Luse static void
960866f15b3SPaul Luse bs_init_cb(void *cb_arg, struct spdk_blob_store *bs,
9618fbbfccdSPaul Luse 	   int bserrno)
9628fbbfccdSPaul Luse {
9638fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
9648fbbfccdSPaul Luse 
9658fbbfccdSPaul Luse 	if (bserrno) {
9668fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in bs init callback",
9678fbbfccdSPaul Luse 			  bserrno);
9688fbbfccdSPaul Luse 		return;
9698fbbfccdSPaul Luse 	}
9708fbbfccdSPaul Luse 	cli_context->bs = bs;
9718fbbfccdSPaul Luse 	printf("blobstore init'd: (%p)\n", cli_context->bs);
9728fbbfccdSPaul Luse 
9738fbbfccdSPaul Luse 	unload_bs(cli_context, "", 0);
9748fbbfccdSPaul Luse }
9758fbbfccdSPaul Luse 
9768fbbfccdSPaul Luse /*
9778fbbfccdSPaul Luse  * Initialize a new blobstore.
9788fbbfccdSPaul Luse  */
9798fbbfccdSPaul Luse static void
9808fbbfccdSPaul Luse init_bs(struct cli_context_t *cli_context)
9818fbbfccdSPaul Luse {
982c8658397SShuhei Matsumoto 	int rc;
9838fbbfccdSPaul Luse 
984c8658397SShuhei Matsumoto 	printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name);
9858fbbfccdSPaul Luse 
986c8658397SShuhei Matsumoto 	rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL,
987c8658397SShuhei Matsumoto 					 &cli_context->bs_dev);
988c8658397SShuhei Matsumoto 	if (rc != 0) {
989c8658397SShuhei Matsumoto 		printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc));
9908fbbfccdSPaul Luse 		spdk_app_stop(-1);
9918fbbfccdSPaul Luse 		return;
9928fbbfccdSPaul Luse 	}
9938fbbfccdSPaul Luse 
994866f15b3SPaul Luse 	spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb,
9958fbbfccdSPaul Luse 		     cli_context);
9968fbbfccdSPaul Luse }
9978fbbfccdSPaul Luse 
998d1d22046SJim Harris static void
999d1d22046SJim Harris spdk_bsdump_done(void *arg, int bserrno)
1000d1d22046SJim Harris {
1001d1d22046SJim Harris 	struct cli_context_t *cli_context = arg;
1002d1d22046SJim Harris 
1003d1d22046SJim Harris 	if (cli_context->cli_mode == CLI_MODE_CMD) {
1004d1d22046SJim Harris 		spdk_app_stop(0);
1005d1d22046SJim Harris 	} else {
1006d1d22046SJim Harris 		cli_context->action = CLI_NONE;
1007deb8ee5cSBen Walker 		cli_start(cli_context);
1008d1d22046SJim Harris 	}
1009d1d22046SJim Harris }
1010d1d22046SJim Harris 
1011d1d22046SJim Harris static void
1012d1d22046SJim Harris bsdump_print_xattr(FILE *fp, const char *bstype, const char *name, const void *value,
1013d1d22046SJim Harris 		   size_t value_len)
1014d1d22046SJim Harris {
1015d1d22046SJim Harris 	if (strncmp(bstype, "BLOBFS", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) {
1016d1d22046SJim Harris 		if (strcmp(name, "name") == 0) {
1017d1d22046SJim Harris 			fprintf(fp, "%.*s", (int)value_len, (char *)value);
1018d1d22046SJim Harris 		} else if (strcmp(name, "length") == 0 && value_len == sizeof(uint64_t)) {
1019d1d22046SJim Harris 			uint64_t length;
1020d1d22046SJim Harris 
1021d1d22046SJim Harris 			memcpy(&length, value, sizeof(length));
1022d1d22046SJim Harris 			fprintf(fp, "%" PRIu64, length);
1023d1d22046SJim Harris 		} else {
1024d1d22046SJim Harris 			fprintf(fp, "?");
1025d1d22046SJim Harris 		}
1026d1d22046SJim Harris 	} else if (strncmp(bstype, "LVOLSTORE", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) {
1027d1d22046SJim Harris 		if (strcmp(name, "name") == 0) {
1028d1d22046SJim Harris 			fprintf(fp, "%s", (char *)value);
1029643385eaSMike Gerdts 		} else if (strcmp(name, "uuid") == 0) {
1030643385eaSMike Gerdts 			struct spdk_uuid uuid;
1031d1d22046SJim Harris 
1032643385eaSMike Gerdts 			if (spdk_uuid_parse(&uuid, (const char *)value) == 0) {
1033643385eaSMike Gerdts 				fprintf(fp, "%s", (const char *)value);
1034643385eaSMike Gerdts 			} else {
1035643385eaSMike Gerdts 				fprintf(fp, "? Invalid UUID");
1036643385eaSMike Gerdts 			}
1037d1d22046SJim Harris 		} else {
1038d1d22046SJim Harris 			fprintf(fp, "?");
1039d1d22046SJim Harris 		}
1040d1d22046SJim Harris 	} else {
1041d1d22046SJim Harris 		fprintf(fp, "?");
1042d1d22046SJim Harris 	}
1043d1d22046SJim Harris }
1044d1d22046SJim Harris 
1045d1d22046SJim Harris /*
1046d1d22046SJim Harris  * Dump metadata of an existing blobstore in a human-readable format.
1047d1d22046SJim Harris  */
1048d1d22046SJim Harris static void
1049d1d22046SJim Harris dump_bs(struct cli_context_t *cli_context)
1050d1d22046SJim Harris {
1051c8658397SShuhei Matsumoto 	int rc;
1052d1d22046SJim Harris 
1053c8658397SShuhei Matsumoto 	printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name);
1054d1d22046SJim Harris 
1055c8658397SShuhei Matsumoto 	rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL,
1056c8658397SShuhei Matsumoto 					 &cli_context->bs_dev);
1057c8658397SShuhei Matsumoto 	if (rc != 0) {
1058c8658397SShuhei Matsumoto 		printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc));
1059d1d22046SJim Harris 		spdk_app_stop(-1);
1060d1d22046SJim Harris 		return;
1061d1d22046SJim Harris 	}
1062d1d22046SJim Harris 
1063d1d22046SJim Harris 	spdk_bs_dump(cli_context->bs_dev, stdout, bsdump_print_xattr, spdk_bsdump_done, cli_context);
1064d1d22046SJim Harris }
1065d1d22046SJim Harris 
10668fbbfccdSPaul Luse /*
1067c5e63c71SPaul Luse  * Common cmd/option parser for command and shell modes.
1068c5e63c71SPaul Luse  */
1069c5e63c71SPaul Luse static bool
1070c5e63c71SPaul Luse cmd_parser(int argc, char **argv, struct cli_context_t *cli_context)
10718fbbfccdSPaul Luse {
10728fbbfccdSPaul Luse 	int op;
1073c5e63c71SPaul Luse 	int cmd_chosen = 0;
1074c5e63c71SPaul Luse 	char resp;
10758fbbfccdSPaul Luse 
10763453391bSMike Gerdts 	while ((op = getopt(argc, argv, "b:d:f:hij:l:m:n:p:r:s:w:DRST:Xx:")) != -1) {
10778fbbfccdSPaul Luse 		switch (op) {
1078cfa4cae0SPaul Luse 		case 'b':
1079cfa4cae0SPaul Luse 			if (strcmp(cli_context->bdev_name, "") == 0) {
1080cfa4cae0SPaul Luse 				snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg);
1081cfa4cae0SPaul Luse 			} else {
1082cfa4cae0SPaul Luse 				printf("Current setting for -b is: %s\n", cli_context->bdev_name);
1083cfa4cae0SPaul Luse 				usage(cli_context, "ERROR: -b option can only be set once.\n");
1084cfa4cae0SPaul Luse 			}
1085cfa4cae0SPaul Luse 			break;
1086d1d22046SJim Harris 		case 'D':
1087d1d22046SJim Harris 			cmd_chosen++;
1088d1d22046SJim Harris 			cli_context->action = CLI_DUMP_BS;
1089d1d22046SJim Harris 			break;
10908fbbfccdSPaul Luse 		case 'd':
1091cf0d7736SPaul Luse 			if (argv[optind] != NULL) {
1092c5e63c71SPaul Luse 				cmd_chosen++;
10934952f254SDaniel Verkamp 				cli_context->action = CLI_DUMP_BLOB;
109476a577b0SMike Gerdts 				cli_context->blobid = spdk_strtoll(optarg, 0);
1095e5044f79SPaul Luse 				snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]);
1096cf0d7736SPaul Luse 			} else {
1097cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
1098cf0d7736SPaul Luse 			}
10998fbbfccdSPaul Luse 			break;
11008fbbfccdSPaul Luse 		case 'f':
1101cf0d7736SPaul Luse 			if (argv[optind] != NULL) {
1102c5e63c71SPaul Luse 				cmd_chosen++;
11038fbbfccdSPaul Luse 				cli_context->action = CLI_FILL;
110476a577b0SMike Gerdts 				cli_context->blobid = spdk_strtoll(optarg, 0);
110576a577b0SMike Gerdts 				cli_context->fill_value = spdk_strtol(argv[optind], 0);
1106cf0d7736SPaul Luse 			} else {
1107cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
1108cf0d7736SPaul Luse 			}
11098fbbfccdSPaul Luse 			break;
1110c5e63c71SPaul Luse 		case 'h':
1111c5e63c71SPaul Luse 			cmd_chosen++;
1112c5e63c71SPaul Luse 			cli_context->action = CLI_HELP;
1113c5e63c71SPaul Luse 			break;
11148fbbfccdSPaul Luse 		case 'i':
1115e5044f79SPaul Luse 			if (cli_context->cli_mode != CLI_MODE_SCRIPT) {
1116e5044f79SPaul Luse 				printf("Your entire blobstore will be destroyed. Are you sure? (y/n) ");
1117c5e63c71SPaul Luse 				if (scanf("%c%*c", &resp)) {
1118c5e63c71SPaul Luse 					if (resp == 'y' || resp == 'Y') {
1119c5e63c71SPaul Luse 						cmd_chosen++;
11208fbbfccdSPaul Luse 						cli_context->action = CLI_INIT_BS;
1121c5e63c71SPaul Luse 					} else {
1122c5e63c71SPaul Luse 						if (cli_context->cli_mode == CLI_MODE_CMD) {
1123e5044f79SPaul Luse 							spdk_app_stop(0);
1124e5044f79SPaul Luse 							return false;
1125c5e63c71SPaul Luse 						}
1126c5e63c71SPaul Luse 					}
1127c5e63c71SPaul Luse 				}
1128e5044f79SPaul Luse 			} else {
1129e5044f79SPaul Luse 				cmd_chosen++;
1130e5044f79SPaul Luse 				cli_context->action = CLI_INIT_BS;
1131e5044f79SPaul Luse 			}
11328fbbfccdSPaul Luse 			break;
11336a41e84cSWANGHAILIANG 		case 'j':
11346a41e84cSWANGHAILIANG 			if (cli_context->app_started == false) {
11356a41e84cSWANGHAILIANG 				cli_context->config_file = optarg;
11366a41e84cSWANGHAILIANG 			} else {
11376a41e84cSWANGHAILIANG 				usage(cli_context, "ERROR: -j option not valid during shell mode.\n");
11386a41e84cSWANGHAILIANG 			}
11396a41e84cSWANGHAILIANG 			break;
11408fbbfccdSPaul Luse 		case 'r':
1141cf0d7736SPaul Luse 			if (argv[optind] != NULL) {
1142c5e63c71SPaul Luse 				cmd_chosen++;
11438fbbfccdSPaul Luse 				cli_context->action = CLI_REM_XATTR;
114476a577b0SMike Gerdts 				cli_context->blobid = spdk_strtoll(optarg, 0);
1145e5044f79SPaul Luse 				snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]);
1146cf0d7736SPaul Luse 			} else {
1147cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
1148cf0d7736SPaul Luse 			}
11498fbbfccdSPaul Luse 			break;
11508fbbfccdSPaul Luse 		case 'l':
11518fbbfccdSPaul Luse 			if (strcmp("bdevs", optarg) == 0) {
1152c5e63c71SPaul Luse 				cmd_chosen++;
11538fbbfccdSPaul Luse 				cli_context->action = CLI_LIST_BDEVS;
11548fbbfccdSPaul Luse 			} else if (strcmp("blobs", optarg) == 0) {
1155c5e63c71SPaul Luse 				cmd_chosen++;
11568fbbfccdSPaul Luse 				cli_context->action = CLI_LIST_BLOBS;
11578fbbfccdSPaul Luse 			} else {
1158e5044f79SPaul Luse 				usage(cli_context, "ERROR: invalid option for list\n");
11598fbbfccdSPaul Luse 			}
11608fbbfccdSPaul Luse 			break;
11618fbbfccdSPaul Luse 		case 'm':
1162cf0d7736SPaul Luse 			if (argv[optind] != NULL) {
1163c5e63c71SPaul Luse 				cmd_chosen++;
11644952f254SDaniel Verkamp 				cli_context->action = CLI_IMPORT_BLOB;
116576a577b0SMike Gerdts 				cli_context->blobid = spdk_strtoll(optarg, 0);
1166e5044f79SPaul Luse 				snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]);
1167cf0d7736SPaul Luse 			} else {
1168cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
1169cf0d7736SPaul Luse 			}
11708fbbfccdSPaul Luse 			break;
11718fbbfccdSPaul Luse 		case 'n':
1172404c5a3fSShuhei Matsumoto 			cli_context->num_clusters = spdk_strtol(optarg, 10);
11738fbbfccdSPaul Luse 			if (cli_context->num_clusters > 0) {
1174cf0d7736SPaul Luse 				cmd_chosen++;
11758fbbfccdSPaul Luse 				cli_context->action = CLI_CREATE_BLOB;
11768fbbfccdSPaul Luse 			} else {
1177e5044f79SPaul Luse 				usage(cli_context, "ERROR: invalid option for new\n");
11788fbbfccdSPaul Luse 			}
11798fbbfccdSPaul Luse 			break;
11808fbbfccdSPaul Luse 		case 'p':
1181c5e63c71SPaul Luse 			cmd_chosen++;
11828fbbfccdSPaul Luse 			cli_context->action = CLI_SET_SUPER;
118376a577b0SMike Gerdts 			cli_context->superid = spdk_strtoll(optarg, 0);
11848fbbfccdSPaul Luse 			break;
1185ff4fd58bSMike Gerdts 		case 'R':
1186ff4fd58bSMike Gerdts 			cmd_chosen++;
1187ff4fd58bSMike Gerdts 			cli_context->action = CLI_RECOVER;
1188ff4fd58bSMike Gerdts 			break;
1189c5e63c71SPaul Luse 		case 'S':
1190c5e63c71SPaul Luse 			if (cli_context->cli_mode == CLI_MODE_CMD) {
1191cf0d7736SPaul Luse 				cmd_chosen++;
1192c5e63c71SPaul Luse 				cli_context->cli_mode = CLI_MODE_SHELL;
1193c5e63c71SPaul Luse 			}
1194c5e63c71SPaul Luse 			cli_context->action = CLI_NONE;
1195c5e63c71SPaul Luse 			break;
11968fbbfccdSPaul Luse 		case 's':
1197c5e63c71SPaul Luse 			cmd_chosen++;
11988fbbfccdSPaul Luse 			if (strcmp("bs", optarg) == 0) {
11998fbbfccdSPaul Luse 				cli_context->action = CLI_SHOW_BS;
12008fbbfccdSPaul Luse 			} else {
12018fbbfccdSPaul Luse 				cli_context->action = CLI_SHOW_BLOB;
120276a577b0SMike Gerdts 				cli_context->blobid = spdk_strtoll(optarg, 0);
12038fbbfccdSPaul Luse 			}
12048fbbfccdSPaul Luse 			break;
1205e5044f79SPaul Luse 		case 'T':
1206e5044f79SPaul Luse 			if (cli_context->cli_mode == CLI_MODE_CMD) {
1207e5044f79SPaul Luse 				cmd_chosen++;
1208e5044f79SPaul Luse 				cli_context->cli_mode = CLI_MODE_SCRIPT;
1209e5044f79SPaul Luse 				if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) {
1210e5044f79SPaul Luse 					g_script.ignore_errors = true;
1211e5044f79SPaul Luse 				} else {
1212e5044f79SPaul Luse 					g_script.ignore_errors = false;
1213e5044f79SPaul Luse 				}
1214e5044f79SPaul Luse 				snprintf(cli_context->script_file, BUFSIZE, "%s", optarg);
1215e5044f79SPaul Luse 			} else {
1216e5044f79SPaul Luse 				cli_context->action = CLI_NONE;
1217e5044f79SPaul Luse 			}
1218e5044f79SPaul Luse 			break;
12193453391bSMike Gerdts 		case 'w':
12203453391bSMike Gerdts 			cmd_chosen++;
12213453391bSMike Gerdts 			cli_context->action = CLI_DELETE_BLOB;
12223453391bSMike Gerdts 			cli_context->blobid = spdk_strtoll(optarg, 0);
12233453391bSMike Gerdts 			break;
1224c5e63c71SPaul Luse 		case 'X':
1225c5e63c71SPaul Luse 			cmd_chosen++;
1226c5e63c71SPaul Luse 			cli_context->action = CLI_SHELL_EXIT;
1227c5e63c71SPaul Luse 			break;
12288fbbfccdSPaul Luse 		case 'x':
1229cf0d7736SPaul Luse 			if (argv[optind] != NULL || argv[optind + 1] != NULL) {
1230c5e63c71SPaul Luse 				cmd_chosen++;
12318fbbfccdSPaul Luse 				cli_context->action = CLI_SET_XATTR;
123276a577b0SMike Gerdts 				cli_context->blobid = spdk_strtoll(optarg, 0);
1233e5044f79SPaul Luse 				snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]);
1234e5044f79SPaul Luse 				snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]);
1235cf0d7736SPaul Luse 			} else {
1236cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
1237cf0d7736SPaul Luse 			}
12388fbbfccdSPaul Luse 			break;
12398fbbfccdSPaul Luse 		default:
1240e5044f79SPaul Luse 			usage(cli_context, "ERROR: invalid option\n");
12418fbbfccdSPaul Luse 		}
1242db26b96fSPaul Luse 		/* only one actual command can be done at a time */
1243db26b96fSPaul Luse 		if (cmd_chosen > 1) {
1244e5044f79SPaul Luse 			usage(cli_context, "Error: Please choose only one command\n");
12458fbbfccdSPaul Luse 		}
12468fbbfccdSPaul Luse 	}
12478fbbfccdSPaul Luse 
1248c5e63c71SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) {
1249e5044f79SPaul Luse 		usage(cli_context, "Error: Please choose a command.\n");
12508fbbfccdSPaul Luse 	}
12518fbbfccdSPaul Luse 
1252cfa4cae0SPaul Luse 	/*
1253cfa4cae0SPaul Luse 	 * We don't check the local boolean because in some modes it will have been set
1254cfa4cae0SPaul Luse 	 * on and earlier command.
1255cfa4cae0SPaul Luse 	 */
1256df5ad409Swanghailiangx 	if ((strcmp(cli_context->bdev_name, "") == 0) && (cli_context->action != CLI_HELP)) {
1257cfa4cae0SPaul Luse 		usage(cli_context, "Error: -b option is required.\n");
1258cfa4cae0SPaul Luse 		cmd_chosen = 0;
1259cfa4cae0SPaul Luse 	}
1260cfa4cae0SPaul Luse 
1261c5e63c71SPaul Luse 	/* in shell mode we'll call getopt multiple times so need to reset its index */
1262c5e63c71SPaul Luse 	optind = 0;
1263db26b96fSPaul Luse 	return (cmd_chosen == 1);
1264c5e63c71SPaul Luse }
1265c5e63c71SPaul Luse 
1266c5e63c71SPaul Luse /*
1267e5044f79SPaul Luse  * In script mode, we parsed a script file at startup and saved off a bunch of cmd
1268e5044f79SPaul Luse  * lines that we now parse with each run of cli_start so we us the same cmd parser
1269e5044f79SPaul Luse  * as cmd and shell modes.
1270e5044f79SPaul Luse  */
1271e5044f79SPaul Luse static bool
1272e5044f79SPaul Luse line_parser(struct cli_context_t *cli_context)
1273e5044f79SPaul Luse {
1274e5044f79SPaul Luse 	bool cmd_chosen;
1275e5044f79SPaul Luse 	char *tok = NULL;
1276*b37db069SXuQi 	char *sp = NULL;
1277e5044f79SPaul Luse 	int blob_num = 0;
1278e5044f79SPaul Luse 	int start_idx = cli_context->argc;
1279e5044f79SPaul Luse 	int i;
1280e5044f79SPaul Luse 
1281e5044f79SPaul Luse 	printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]);
1282*b37db069SXuQi 	tok = strtok_r(g_script.cmdline[g_script.cmdline_idx], " ", &sp);
1283e5044f79SPaul Luse 	while (tok != NULL) {
1284e5044f79SPaul Luse 		/*
1285e5044f79SPaul Luse 		 * We support one replaceable token right now, a $Bn
1286e5044f79SPaul Luse 		 * represents the blobid that was created in position n
1287e5044f79SPaul Luse 		 * so fish this out now and use it here.
1288e5044f79SPaul Luse 		 */
1289e5044f79SPaul Luse 		cli_context->argv[cli_context->argc] = strdup(tok);
1290e5044f79SPaul Luse 		if (tok[0] == '$' && tok[1] == 'B') {
1291e5044f79SPaul Luse 			tok += 2;
1292404c5a3fSShuhei Matsumoto 			blob_num = spdk_strtol(tok, 10);
12935589fc4aSPaul Luse 			if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) {
1294e5044f79SPaul Luse 				cli_context->argv[cli_context->argc] =
1295e5044f79SPaul Luse 					realloc(cli_context->argv[cli_context->argc], BUFSIZE);
1296e5044f79SPaul Luse 				if (cli_context->argv[cli_context->argc] == NULL) {
1297e5044f79SPaul Luse 					printf("ERROR: unable to realloc memory\n");
1298e5044f79SPaul Luse 					spdk_app_stop(-1);
1299e5044f79SPaul Luse 				}
1300e5044f79SPaul Luse 				if (g_script.blobid[blob_num] == 0) {
1301e5044f79SPaul Luse 					printf("ERROR: There is no blob for $B%d\n",
1302e5044f79SPaul Luse 					       blob_num);
1303e5044f79SPaul Luse 				}
1304e5044f79SPaul Luse 				snprintf(cli_context->argv[cli_context->argc], BUFSIZE,
1305e5044f79SPaul Luse 					 "%" PRIu64, g_script.blobid[blob_num]);
13065589fc4aSPaul Luse 			} else {
13075589fc4aSPaul Luse 				printf("ERROR: Invalid token or exceeded max blobs of %d\n",
13085589fc4aSPaul Luse 				       MAX_SCRIPT_BLOBS);
13095589fc4aSPaul Luse 			}
1310e5044f79SPaul Luse 		}
1311e5044f79SPaul Luse 		cli_context->argc++;
1312*b37db069SXuQi 		tok = strtok_r(NULL, " ", &sp);
1313e5044f79SPaul Luse 	}
1314e5044f79SPaul Luse 
1315e5044f79SPaul Luse 	/* call parse cmd line with user input as args */
1316e5044f79SPaul Luse 	cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context);
1317e5044f79SPaul Luse 
1318e5044f79SPaul Luse 	/* free strdup memory and reset arg count for next shell interaction */
1319e5044f79SPaul Luse 	for (i = start_idx; i < cli_context->argc; i++) {
1320e5044f79SPaul Luse 		free(cli_context->argv[i]);
1321e5044f79SPaul Luse 		cli_context->argv[i] = NULL;
1322e5044f79SPaul Luse 	}
1323e5044f79SPaul Luse 	cli_context->argc = 1;
1324e5044f79SPaul Luse 
1325e5044f79SPaul Luse 	g_script.cmdline_idx++;
1326e5044f79SPaul Luse 	assert(g_script.cmdline_idx < MAX_SCRIPT_LINES);
1327e5044f79SPaul Luse 
1328e5044f79SPaul Luse 	if (cmd_chosen == false) {
1329e5044f79SPaul Luse 		printf("ERROR: Invalid script line starting with: %s\n\n",
1330e5044f79SPaul Luse 		       g_script.cmdline[g_script.cmdline_idx - 1]);
1331e5044f79SPaul Luse 		if (g_script.ignore_errors == false) {
1332e5044f79SPaul Luse 			printf("** Aborting **\n");
1333e5044f79SPaul Luse 			cli_context->action = CLI_SHELL_EXIT;
1334e5044f79SPaul Luse 			cmd_chosen = true;
1335e5044f79SPaul Luse 			unload_bs(cli_context, "", 0);
1336e5044f79SPaul Luse 		} else {
1337e5044f79SPaul Luse 			printf("** Skipping **\n");
1338e5044f79SPaul Luse 		}
1339e5044f79SPaul Luse 	}
1340e5044f79SPaul Luse 
1341e5044f79SPaul Luse 	return cmd_chosen;
1342e5044f79SPaul Luse }
1343e5044f79SPaul Luse 
1344e5044f79SPaul Luse /*
1345e5044f79SPaul Luse  * For script mode, we read a series of commands from a text file and store them
1346e5044f79SPaul Luse  * in a global struct. That, along with the cli_mode that tells us we're in
1347e5044f79SPaul Luse  * script mode is what feeds the rest of the app in the same way as is it were
1348e5044f79SPaul Luse  * getting commands from shell mode.
1349e5044f79SPaul Luse  */
1350e5044f79SPaul Luse static void
1351e5044f79SPaul Luse parse_script(struct cli_context_t *cli_context)
1352e5044f79SPaul Luse {
1353e5044f79SPaul Luse 	FILE *fp = NULL;
1354e5044f79SPaul Luse 	size_t bufsize = BUFSIZE;
1355e5044f79SPaul Luse 	int64_t bytes_in = 0;
1356e5044f79SPaul Luse 	int i = 0;
1357e5044f79SPaul Luse 
1358e5044f79SPaul Luse 	/* initialize global script values */
1359e5044f79SPaul Luse 	for (i = 0; i < MAX_SCRIPT_BLOBS; i++) {
1360e5044f79SPaul Luse 		g_script.blobid[i] = 0;
1361e5044f79SPaul Luse 	}
1362e5044f79SPaul Luse 	g_script.blobid_idx = 0;
1363e5044f79SPaul Luse 	g_script.cmdline_idx = 0;
1364e5044f79SPaul Luse 	i = 0;
1365e5044f79SPaul Luse 
1366e5044f79SPaul Luse 	fp = fopen(cli_context->script_file, "r");
1367e5044f79SPaul Luse 	if (fp == NULL) {
1368e5044f79SPaul Luse 		printf("ERROR: unable to open script: %s\n",
1369e5044f79SPaul Luse 		       cli_context->script_file);
1370e5044f79SPaul Luse 		cli_cleanup(cli_context);
1371e5044f79SPaul Luse 		exit(-1);
1372e5044f79SPaul Luse 	}
1373e5044f79SPaul Luse 
1374e5044f79SPaul Luse 	do {
1375e5044f79SPaul Luse 		bytes_in = getline(&g_script.cmdline[i], &bufsize, fp);
1376e5044f79SPaul Luse 		if (bytes_in > 0) {
1377e5044f79SPaul Luse 			/* replace newline with null */
1378e5044f79SPaul Luse 			spdk_str_chomp(g_script.cmdline[i]);
1379e5044f79SPaul Luse 
1380e5044f79SPaul Luse 			/* ignore comments */
1381e5044f79SPaul Luse 			if (g_script.cmdline[i][0] != '#') {
1382e5044f79SPaul Luse 				i++;
1383e5044f79SPaul Luse 			}
1384e5044f79SPaul Luse 		}
13855589fc4aSPaul Luse 	} while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1);
1386e5044f79SPaul Luse 	fclose(fp);
1387e5044f79SPaul Luse 
1388e5044f79SPaul Luse 	/* add an exit cmd in case they didn't */
1389e5044f79SPaul Luse 	g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE);
1390e5044f79SPaul Luse 	if (g_script.cmdline[i] == NULL)  {
1391e5044f79SPaul Luse 		int j;
1392e5044f79SPaul Luse 
1393e5044f79SPaul Luse 		for (j = 0; j < i; j++) {
1394e5044f79SPaul Luse 			free(g_script.cmdline[j]);
1395e5044f79SPaul Luse 			g_script.cmdline[j] = NULL;
1396e5044f79SPaul Luse 		}
1397e5044f79SPaul Luse 		unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0);
1398e5044f79SPaul Luse 	}
1399e5044f79SPaul Luse 	snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X");
1400e5044f79SPaul Luse 	g_script.max_index = i;
1401e5044f79SPaul Luse }
1402e5044f79SPaul Luse 
1403e5044f79SPaul Luse /*
1404c5e63c71SPaul Luse  * Provides for a shell interface as opposed to one shot command line.
1405c5e63c71SPaul Luse  */
1406c5e63c71SPaul Luse static bool
1407c5e63c71SPaul Luse cli_shell(void *arg1, void *arg2)
1408c5e63c71SPaul Luse {
1409c5e63c71SPaul Luse 	struct cli_context_t *cli_context = arg1;
1410c5e63c71SPaul Luse 	char *line = NULL;
1411c5e63c71SPaul Luse 	ssize_t buf_size = 0;
1412c5e63c71SPaul Luse 	ssize_t bytes_in = 0;
1413c5e63c71SPaul Luse 	ssize_t tok_len = 0;
1414c5e63c71SPaul Luse 	char *tok = NULL;
1415*b37db069SXuQi 	char *sp = NULL;
1416c5e63c71SPaul Luse 	bool cmd_chosen = false;
1417c5e63c71SPaul Luse 	int start_idx = cli_context->argc;
1418c5e63c71SPaul Luse 	int i;
1419c5e63c71SPaul Luse 
1420c5e63c71SPaul Luse 	printf("blob> ");
1421c5e63c71SPaul Luse 	bytes_in = getline(&line, &buf_size, stdin);
1422c5e63c71SPaul Luse 
1423168bc9f1SDaniel Verkamp 	/* If getline() failed (EOF), exit the shell. */
1424168bc9f1SDaniel Verkamp 	if (bytes_in < 0) {
1425168bc9f1SDaniel Verkamp 		free(line);
1426168bc9f1SDaniel Verkamp 		cli_context->action = CLI_SHELL_EXIT;
1427168bc9f1SDaniel Verkamp 		return true;
1428168bc9f1SDaniel Verkamp 	}
1429168bc9f1SDaniel Verkamp 
1430c5e63c71SPaul Luse 	/* parse input and update cli_context so we can use common option parser */
1431c5e63c71SPaul Luse 	if (bytes_in > 0) {
1432*b37db069SXuQi 		tok = strtok_r(line, " ", &sp);
1433c5e63c71SPaul Luse 	}
1434c5e63c71SPaul Luse 	while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) {
1435c5e63c71SPaul Luse 		cli_context->argv[cli_context->argc] = strdup(tok);
1436c5e63c71SPaul Luse 		tok_len = strlen(tok);
1437c5e63c71SPaul Luse 		cli_context->argc++;
1438*b37db069SXuQi 		tok = strtok_r(NULL, " ", &sp);
1439c5e63c71SPaul Luse 	}
1440c5e63c71SPaul Luse 
1441c5e63c71SPaul Luse 	/* replace newline on last arg with null */
1442c5e63c71SPaul Luse 	if (tok_len) {
1443c5e63c71SPaul Luse 		spdk_str_chomp(cli_context->argv[cli_context->argc - 1]);
1444c5e63c71SPaul Luse 	}
1445c5e63c71SPaul Luse 
1446c5e63c71SPaul Luse 	/* call parse cmd line with user input as args */
1447c5e63c71SPaul Luse 	cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context);
1448c5e63c71SPaul Luse 
1449c5e63c71SPaul Luse 	/* free strdup mem & reset arg count for next shell interaction */
1450c5e63c71SPaul Luse 	for (i = start_idx; i < cli_context->argc; i++) {
1451c5e63c71SPaul Luse 		free(cli_context->argv[i]);
1452d4e565b4SPaul Luse 		cli_context->argv[i] = NULL;
1453c5e63c71SPaul Luse 	}
1454c5e63c71SPaul Luse 	cli_context->argc = 1;
1455c5e63c71SPaul Luse 
1456c5e63c71SPaul Luse 	free(line);
1457c5e63c71SPaul Luse 
1458c5e63c71SPaul Luse 	return cmd_chosen;
1459c5e63c71SPaul Luse }
1460c5e63c71SPaul Luse 
1461866f15b3SPaul Luse /*
1462866f15b3SPaul Luse  * This is the function we pass into the SPDK framework that gets
1463866f15b3SPaul Luse  * called first.
1464866f15b3SPaul Luse  */
1465866f15b3SPaul Luse static void
1466deb8ee5cSBen Walker cli_start(void *arg1)
1467866f15b3SPaul Luse {
1468866f15b3SPaul Luse 	struct cli_context_t *cli_context = arg1;
1469866f15b3SPaul Luse 
1470866f15b3SPaul Luse 	/*
1471e5044f79SPaul Luse 	 * If we're in script mode, we already have a list of commands so
1472e5044f79SPaul Luse 	 * just need to pull them out one at a time and process them.
1473e5044f79SPaul Luse 	 */
1474e5044f79SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
1475e5044f79SPaul Luse 		while (line_parser(cli_context) == false);
1476e5044f79SPaul Luse 	}
1477e5044f79SPaul Luse 
1478e5044f79SPaul Luse 	/*
1479866f15b3SPaul Luse 	 * The initial cmd line options are parsed once before this function is
1480866f15b3SPaul Luse 	 * called so if there is no action, we're in shell mode and will loop
1481866f15b3SPaul Luse 	 * here until a a valid option is parsed and returned.
1482866f15b3SPaul Luse 	 */
1483866f15b3SPaul Luse 	if (cli_context->action == CLI_NONE) {
1484866f15b3SPaul Luse 		while (cli_shell(cli_context, NULL) == false);
1485866f15b3SPaul Luse 	}
1486866f15b3SPaul Luse 
1487866f15b3SPaul Luse 	/* Decide what to do next based on cmd line parsing. */
1488866f15b3SPaul Luse 	switch (cli_context->action) {
1489866f15b3SPaul Luse 	case CLI_SET_SUPER:
1490866f15b3SPaul Luse 	case CLI_SHOW_BS:
1491866f15b3SPaul Luse 	case CLI_CREATE_BLOB:
1492866f15b3SPaul Luse 	case CLI_SET_XATTR:
1493866f15b3SPaul Luse 	case CLI_REM_XATTR:
1494866f15b3SPaul Luse 	case CLI_SHOW_BLOB:
1495866f15b3SPaul Luse 	case CLI_LIST_BLOBS:
14964952f254SDaniel Verkamp 	case CLI_DUMP_BLOB:
14974952f254SDaniel Verkamp 	case CLI_IMPORT_BLOB:
1498866f15b3SPaul Luse 	case CLI_FILL:
1499ff4fd58bSMike Gerdts 	case CLI_RECOVER:
15003453391bSMike Gerdts 	case CLI_DELETE_BLOB:
1501866f15b3SPaul Luse 		load_bs(cli_context);
1502866f15b3SPaul Luse 		break;
1503866f15b3SPaul Luse 	case CLI_INIT_BS:
1504866f15b3SPaul Luse 		init_bs(cli_context);
1505866f15b3SPaul Luse 		break;
1506d1d22046SJim Harris 	case CLI_DUMP_BS:
1507d1d22046SJim Harris 		dump_bs(cli_context);
1508d1d22046SJim Harris 		break;
1509866f15b3SPaul Luse 	case CLI_LIST_BDEVS:
1510866f15b3SPaul Luse 		list_bdevs(cli_context);
1511866f15b3SPaul Luse 		break;
1512866f15b3SPaul Luse 	case CLI_SHELL_EXIT:
1513866f15b3SPaul Luse 		/*
1514866f15b3SPaul Luse 		 * Because shell mode reuses cmd mode functions, the blobstore
1515866f15b3SPaul Luse 		 * is loaded/unloaded with every action so we just need to
1516866f15b3SPaul Luse 		 * stop the framework. For this app there's no need to optimize
1517866f15b3SPaul Luse 		 * and keep the blobstore open while the app is in shell mode.
1518866f15b3SPaul Luse 		 */
1519866f15b3SPaul Luse 		spdk_app_stop(0);
1520866f15b3SPaul Luse 		break;
1521866f15b3SPaul Luse 	case CLI_HELP:
1522e5044f79SPaul Luse 		usage(cli_context, "");
1523866f15b3SPaul Luse 		unload_complete(cli_context, 0);
1524866f15b3SPaul Luse 		break;
1525866f15b3SPaul Luse 	default:
1526866f15b3SPaul Luse 		/* should never get here */
152798a2ec3eSPaul Luse 		exit(-1);
1528866f15b3SPaul Luse 		break;
1529866f15b3SPaul Luse 	}
1530866f15b3SPaul Luse }
1531c5e63c71SPaul Luse 
1532c5e63c71SPaul Luse int
1533c5e63c71SPaul Luse main(int argc, char **argv)
1534c5e63c71SPaul Luse {
1535c5e63c71SPaul Luse 	struct spdk_app_opts opts = {};
1536c5e63c71SPaul Luse 	struct cli_context_t *cli_context = NULL;
1537e5044f79SPaul Luse 	bool cmd_chosen;
1538c5e63c71SPaul Luse 	int rc = 0;
1539c5e63c71SPaul Luse 
1540c5e63c71SPaul Luse 	if (argc < 2) {
1541e5044f79SPaul Luse 		usage(cli_context, "ERROR: Invalid option\n");
1542e5044f79SPaul Luse 		exit(-1);
1543c5e63c71SPaul Luse 	}
1544c5e63c71SPaul Luse 
1545c5e63c71SPaul Luse 	cli_context = calloc(1, sizeof(struct cli_context_t));
1546c5e63c71SPaul Luse 	if (cli_context == NULL) {
1547c5e63c71SPaul Luse 		printf("ERROR: could not allocate context structure\n");
1548c5e63c71SPaul Luse 		exit(-1);
1549c5e63c71SPaul Luse 	}
1550cfa4cae0SPaul Luse 
1551c5e63c71SPaul Luse 	/* default to CMD mode until we've parsed the first parms */
1552c5e63c71SPaul Luse 	cli_context->cli_mode = CLI_MODE_CMD;
1553c5e63c71SPaul Luse 	cli_context->argv[0] = strdup(argv[0]);
1554c5e63c71SPaul Luse 	cli_context->argc = 1;
1555c5e63c71SPaul Luse 
1556c5e63c71SPaul Luse 	/* parse command line */
1557e5044f79SPaul Luse 	cmd_chosen = cmd_parser(argc, argv, cli_context);
1558c5e63c71SPaul Luse 	free(cli_context->argv[0]);
1559e5044f79SPaul Luse 	cli_context->argv[0] = NULL;
1560e5044f79SPaul Luse 	if (cmd_chosen == false) {
1561e5044f79SPaul Luse 		cli_cleanup(cli_context);
1562e5044f79SPaul Luse 		exit(-1);
1563e5044f79SPaul Luse 	}
1564c5e63c71SPaul Luse 
1565c5e63c71SPaul Luse 	/* after displaying help, just exit */
1566c5e63c71SPaul Luse 	if (cli_context->action == CLI_HELP) {
1567e5044f79SPaul Luse 		usage(cli_context, "");
1568c5e63c71SPaul Luse 		cli_cleanup(cli_context);
1569c5e63c71SPaul Luse 		exit(-1);
1570c5e63c71SPaul Luse 	}
1571c5e63c71SPaul Luse 
1572c5e63c71SPaul Luse 	/* if they don't supply a conf name, use the default */
1573c5e63c71SPaul Luse 	if (!cli_context->config_file) {
1574c5e63c71SPaul Luse 		cli_context->config_file = program_conf;
1575c5e63c71SPaul Luse 	}
1576c5e63c71SPaul Luse 
1577c5e63c71SPaul Luse 	/* if the config file doesn't exist, tell them how to make one */
1578c5e63c71SPaul Luse 	if (access(cli_context->config_file, F_OK) == -1) {
1579c5e63c71SPaul Luse 		printf("Error: No config file found.\n");
15806a41e84cSWANGHAILIANG 		printf("To create a config file named 'blobcli.json' for your NVMe device:\n");
15818c3e71f0STomasz Zawadzki 		printf("   <path to spdk>/scripts/gen_nvme.sh --json-with-subsystems > blobcli.json\n");
1582c5e63c71SPaul Luse 		printf("and then re-run the cli tool.\n");
158398a2ec3eSPaul Luse 		exit(-1);
1584c5e63c71SPaul Luse 	}
1585c5e63c71SPaul Luse 
1586e5044f79SPaul Luse 	/*
1587e5044f79SPaul Luse 	 * For script mode we keep a bunch of stuff in a global since
1588e5044f79SPaul Luse 	 * none if it is passed back and forth to SPDK.
1589e5044f79SPaul Luse 	 */
1590e5044f79SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
1591e5044f79SPaul Luse 		/*
1592e5044f79SPaul Luse 		 * Now we'll build up the global which will direct this run of the app
1593e5044f79SPaul Luse 		 * as it will have a list (g_script) of all of the commands line by
1594e5044f79SPaul Luse 		 * line as if they were typed in on the shell at cmd line.
1595e5044f79SPaul Luse 		 */
1596e5044f79SPaul Luse 		parse_script(cli_context);
1597e5044f79SPaul Luse 	}
1598e5044f79SPaul Luse 
15998fbbfccdSPaul Luse 	/* Set default values in opts struct along with name and conf file. */
160048701bd9SZiye Yang 	spdk_app_opts_init(&opts, sizeof(opts));
16018fbbfccdSPaul Luse 	opts.name = "blobcli";
16026a41e84cSWANGHAILIANG 	opts.json_config_file = cli_context->config_file;
16035db859daSKrzysztof Karas 	opts.rpc_addr = NULL;
16048fbbfccdSPaul Luse 
1605c5e63c71SPaul Luse 	cli_context->app_started = true;
160636287957SBen Walker 	rc = spdk_app_start(&opts, cli_start, cli_context);
16078fbbfccdSPaul Luse 	if (rc) {
16088fbbfccdSPaul Luse 		printf("ERROR!\n");
16098fbbfccdSPaul Luse 	}
16108fbbfccdSPaul Luse 
16118fbbfccdSPaul Luse 	/* Free up memory that we allocated */
16128fbbfccdSPaul Luse 	cli_cleanup(cli_context);
16138fbbfccdSPaul Luse 
16148fbbfccdSPaul Luse 	/* Gracefully close out all of the SPDK subsystems. */
16158fbbfccdSPaul Luse 	spdk_app_fini();
16168fbbfccdSPaul Luse 	return rc;
16178fbbfccdSPaul Luse }
1618