xref: /spdk/examples/blob/cli/blobcli.c (revision c8efd8a8b2c8cb9ab757640047b5a4544cf49f2f)
18fbbfccdSPaul Luse /*-
28fbbfccdSPaul Luse  *   BSD LICENSE
38fbbfccdSPaul Luse  *
48fbbfccdSPaul Luse  *   Copyright (c) Intel Corporation.
58fbbfccdSPaul Luse  *   All rights reserved.
68fbbfccdSPaul Luse  *
78fbbfccdSPaul Luse  *   Redistribution and use in source and binary forms, with or without
88fbbfccdSPaul Luse  *   modification, are permitted provided that the following conditions
98fbbfccdSPaul Luse  *   are met:
108fbbfccdSPaul Luse  *
118fbbfccdSPaul Luse  *     * Redistributions of source code must retain the above copyright
128fbbfccdSPaul Luse  *       notice, this list of conditions and the following disclaimer.
138fbbfccdSPaul Luse  *     * Redistributions in binary form must reproduce the above copyright
148fbbfccdSPaul Luse  *       notice, this list of conditions and the following disclaimer in
158fbbfccdSPaul Luse  *       the documentation and/or other materials provided with the
168fbbfccdSPaul Luse  *       distribution.
178fbbfccdSPaul Luse  *     * Neither the name of Intel Corporation nor the names of its
188fbbfccdSPaul Luse  *       contributors may be used to endorse or promote products derived
198fbbfccdSPaul Luse  *       from this software without specific prior written permission.
208fbbfccdSPaul Luse  *
218fbbfccdSPaul Luse  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
228fbbfccdSPaul Luse  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
238fbbfccdSPaul Luse  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
248fbbfccdSPaul Luse  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
258fbbfccdSPaul Luse  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
268fbbfccdSPaul Luse  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
278fbbfccdSPaul Luse  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
288fbbfccdSPaul Luse  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
298fbbfccdSPaul Luse  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
308fbbfccdSPaul Luse  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
318fbbfccdSPaul Luse  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
328fbbfccdSPaul Luse  */
338fbbfccdSPaul Luse 
348fbbfccdSPaul Luse #include "spdk/stdinc.h"
358fbbfccdSPaul Luse 
368fbbfccdSPaul Luse #include "spdk/bdev.h"
378fbbfccdSPaul Luse #include "spdk/env.h"
388fbbfccdSPaul Luse #include "spdk/event.h"
398fbbfccdSPaul Luse #include "spdk/blob_bdev.h"
408fbbfccdSPaul Luse #include "spdk/blob.h"
418fbbfccdSPaul Luse #include "spdk/log.h"
428fbbfccdSPaul Luse #include "spdk/version.h"
43c5e63c71SPaul Luse #include "spdk/string.h"
448fbbfccdSPaul Luse 
458fbbfccdSPaul Luse /*
46866f15b3SPaul Luse  * The following is not a public header file, but the CLI does expose
478fbbfccdSPaul Luse  * some internals of blobstore for dev/debug puposes so we
488fbbfccdSPaul Luse  * include it here.
498fbbfccdSPaul Luse  */
508fbbfccdSPaul Luse #include "../lib/blob/blobstore.h"
51c5e63c71SPaul Luse static void cli_start(void *arg1, void *arg2);
528fbbfccdSPaul Luse 
538fbbfccdSPaul Luse static const char *program_name = "blobcli";
54866f15b3SPaul Luse /* default name for .conf file, any name can be used however with -c switch */
558fbbfccdSPaul Luse static const char *program_conf = "blobcli.conf";
568fbbfccdSPaul Luse 
57c5e63c71SPaul Luse /*
58c5e63c71SPaul Luse  * CMD mode runs one command at a time which can be annoying as the init takes
59c5e63c71SPaul Luse  * a few seconds, so the shell mode, invoked with -S, does the init once and gives
60e5044f79SPaul Luse  * the user an interactive shell instead. With script mode init is also done just
61e5044f79SPaul Luse  * once.
62c5e63c71SPaul Luse  */
63c5e63c71SPaul Luse enum cli_mode_type {
64c5e63c71SPaul Luse 	CLI_MODE_CMD,
65e5044f79SPaul Luse 	CLI_MODE_SHELL,
66e5044f79SPaul Luse 	CLI_MODE_SCRIPT
67c5e63c71SPaul Luse };
68c5e63c71SPaul Luse 
698fbbfccdSPaul Luse enum cli_action_type {
70c5e63c71SPaul Luse 	CLI_NONE,
714952f254SDaniel Verkamp 	CLI_IMPORT_BLOB,
724952f254SDaniel Verkamp 	CLI_DUMP_BLOB,
738fbbfccdSPaul Luse 	CLI_FILL,
748fbbfccdSPaul Luse 	CLI_REM_XATTR,
758fbbfccdSPaul Luse 	CLI_SET_XATTR,
768fbbfccdSPaul Luse 	CLI_SET_SUPER,
778fbbfccdSPaul Luse 	CLI_SHOW_BS,
788fbbfccdSPaul Luse 	CLI_SHOW_BLOB,
798fbbfccdSPaul Luse 	CLI_CREATE_BLOB,
808fbbfccdSPaul Luse 	CLI_LIST_BDEVS,
818fbbfccdSPaul Luse 	CLI_LIST_BLOBS,
82c5e63c71SPaul Luse 	CLI_INIT_BS,
83c5e63c71SPaul Luse 	CLI_SHELL_EXIT,
84e5044f79SPaul Luse 	CLI_HELP,
858fbbfccdSPaul Luse };
868fbbfccdSPaul Luse 
87866f15b3SPaul Luse #define BUFSIZE 255
88e5044f79SPaul Luse #define MAX_ARGS 16
89866f15b3SPaul Luse #define ALIGN_4K 4096
90866f15b3SPaul Luse #define STARTING_PAGE 0
91866f15b3SPaul Luse #define NUM_PAGES 1
92e5044f79SPaul Luse 
93c5e63c71SPaul Luse /*
94c5e63c71SPaul Luse  * The CLI uses the SPDK app framework so is async and callback driven. A
95c5e63c71SPaul Luse  * pointer to this structure is passed to SPDK calls and returned in the
96c5e63c71SPaul Luse  * callbacks for easy access to all the info we may need.
97c5e63c71SPaul Luse  */
988fbbfccdSPaul Luse struct cli_context_t {
998fbbfccdSPaul Luse 	struct spdk_blob_store *bs;
1008fbbfccdSPaul Luse 	struct spdk_blob *blob;
101c5e63c71SPaul Luse 	struct spdk_bs_dev *bs_dev;
1028fbbfccdSPaul Luse 	spdk_blob_id blobid;
1038fbbfccdSPaul Luse 	spdk_blob_id superid;
1048fbbfccdSPaul Luse 	struct spdk_io_channel *channel;
1058fbbfccdSPaul Luse 	uint8_t *buff;
1068fbbfccdSPaul Luse 	uint64_t page_size;
1078fbbfccdSPaul Luse 	uint64_t page_count;
1088fbbfccdSPaul Luse 	uint64_t blob_pages;
1098fbbfccdSPaul Luse 	uint64_t bytes_so_far;
1108fbbfccdSPaul Luse 	FILE *fp;
1118fbbfccdSPaul Luse 	enum cli_action_type action;
1128fbbfccdSPaul Luse 	char key[BUFSIZE + 1];
1138fbbfccdSPaul Luse 	char value[BUFSIZE + 1];
1148fbbfccdSPaul Luse 	char file[BUFSIZE + 1];
1158fbbfccdSPaul Luse 	uint64_t filesize;
1168fbbfccdSPaul Luse 	int fill_value;
117cfa4cae0SPaul Luse 	char bdev_name[BUFSIZE];
1188fbbfccdSPaul Luse 	int rc;
1198fbbfccdSPaul Luse 	int num_clusters;
120c5e63c71SPaul Luse 	enum cli_mode_type cli_mode;
121c5e63c71SPaul Luse 	const char *config_file;
122c5e63c71SPaul Luse 	int argc;
123c5e63c71SPaul Luse 	char *argv[MAX_ARGS];
124c5e63c71SPaul Luse 	bool app_started;
125e5044f79SPaul Luse 	char script_file[BUFSIZE + 1];
1268fbbfccdSPaul Luse };
1278fbbfccdSPaul Luse 
128e5044f79SPaul Luse /* we store a bunch of stuff in a global struct for use by scripting mode */
129e5044f79SPaul Luse #define MAX_SCRIPT_LINES 64
130e5044f79SPaul Luse #define MAX_SCRIPT_BLOBS 16
131e5044f79SPaul Luse struct cli_script_t {
132e5044f79SPaul Luse 	spdk_blob_id blobid[MAX_SCRIPT_BLOBS];
133e5044f79SPaul Luse 	int blobid_idx;
134e5044f79SPaul Luse 	int max_index;
135e5044f79SPaul Luse 	int cmdline_idx;
136e5044f79SPaul Luse 	bool ignore_errors;
137e5044f79SPaul Luse 	char *cmdline[MAX_SCRIPT_LINES];
138e5044f79SPaul Luse };
139e5044f79SPaul Luse struct cli_script_t g_script;
140e5044f79SPaul Luse 
1418fbbfccdSPaul Luse /*
142c5e63c71SPaul Luse  * Common printing of commands for CLI and shell modes.
143c5e63c71SPaul Luse  */
144c5e63c71SPaul Luse static void
145e5044f79SPaul Luse print_cmds(void)
146c5e63c71SPaul Luse {
147c5e63c71SPaul Luse 	printf("\nCommands include:\n");
148cfa4cae0SPaul Luse 	printf("\t-b bdev - name of the block device to use (example: Nvme0n1)\n");
149c5e63c71SPaul Luse 	printf("\t-d <blobid> filename - dump contents of a blob to a file\n");
150c5e63c71SPaul Luse 	printf("\t-f <blobid> value - fill a blob with a decimal value\n");
151c5e63c71SPaul Luse 	printf("\t-h - this help screen\n");
152c5e63c71SPaul Luse 	printf("\t-i - initialize a blobstore\n");
153c5e63c71SPaul Luse 	printf("\t-l bdevs | blobs - list either available bdevs or existing blobs\n");
154c5e63c71SPaul Luse 	printf("\t-m <blobid> filename - import contents of a file to a blob\n");
155c5e63c71SPaul Luse 	printf("\t-n <# clusters> - create new blob\n");
156c5e63c71SPaul Luse 	printf("\t-p <blobid> - set the superblob to the ID provided\n");
157c5e63c71SPaul Luse 	printf("\t-r <blobid> name - remove xattr name/value pair\n");
158c5e63c71SPaul Luse 	printf("\t-s <blobid> | bs - show blob info or blobstore info\n");
159c5e63c71SPaul Luse 	printf("\t-x <blobid> name value - set xattr name/value pair\n");
160c5e63c71SPaul Luse 	printf("\t-X - exit when in interactive shell mode\n");
161c5e63c71SPaul Luse 	printf("\t-S - enter interactive shell mode\n");
162e5044f79SPaul Luse 	printf("\t-T <filename> - automated script mode\n");
163c5e63c71SPaul Luse 	printf("\n");
164c5e63c71SPaul Luse }
165c5e63c71SPaul Luse 
166c5e63c71SPaul Luse /*
1678fbbfccdSPaul Luse  * Prints usage and relevant error message.
1688fbbfccdSPaul Luse  */
1698fbbfccdSPaul Luse static void
170e5044f79SPaul Luse usage(struct cli_context_t *cli_context, char *msg)
1718fbbfccdSPaul Luse {
1728fbbfccdSPaul Luse 	if (msg) {
1738fbbfccdSPaul Luse 		printf("%s", msg);
1748fbbfccdSPaul Luse 	}
175e5044f79SPaul Luse 
1767fc14171SDaniel Verkamp 	if (!cli_context || cli_context->cli_mode == CLI_MODE_CMD) {
1778fbbfccdSPaul Luse 		printf("Version %s\n", SPDK_VERSION_STRING);
1788fbbfccdSPaul Luse 		printf("Usage: %s [-c SPDK config_file] Command\n", program_name);
1798fbbfccdSPaul Luse 		printf("\n%s is a command line tool for interacting with blobstore\n",
1808fbbfccdSPaul Luse 		       program_name);
1818fbbfccdSPaul Luse 		printf("on the underlying device specified in the conf file passed\n");
1828fbbfccdSPaul Luse 		printf("in as a command line option.\n");
183e5044f79SPaul Luse 	}
1847fc14171SDaniel Verkamp 	if (!cli_context || cli_context->cli_mode != CLI_MODE_SCRIPT) {
185e5044f79SPaul Luse 		print_cmds();
186e5044f79SPaul Luse 	}
1878fbbfccdSPaul Luse }
1888fbbfccdSPaul Luse 
1898fbbfccdSPaul Luse /*
1908fbbfccdSPaul Luse  * Free up memory that we allocated.
1918fbbfccdSPaul Luse  */
1928fbbfccdSPaul Luse static void
1938fbbfccdSPaul Luse cli_cleanup(struct cli_context_t *cli_context)
1948fbbfccdSPaul Luse {
1958fbbfccdSPaul Luse 	if (cli_context->buff) {
1968fbbfccdSPaul Luse 		spdk_dma_free(cli_context->buff);
1978fbbfccdSPaul Luse 	}
198e5044f79SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
199e5044f79SPaul Luse 		int i;
200e5044f79SPaul Luse 
201e5044f79SPaul Luse 		for (i = 0; i <= g_script.max_index; i++) {
202e5044f79SPaul Luse 			free(g_script.cmdline[i]);
203e5044f79SPaul Luse 		}
204e5044f79SPaul Luse 	}
2058fbbfccdSPaul Luse 	free(cli_context);
2068fbbfccdSPaul Luse }
2078fbbfccdSPaul Luse 
2088fbbfccdSPaul Luse /*
2098fbbfccdSPaul Luse  * Callback routine for the blobstore unload.
2108fbbfccdSPaul Luse  */
2118fbbfccdSPaul Luse static void
2128fbbfccdSPaul Luse unload_complete(void *cb_arg, int bserrno)
2138fbbfccdSPaul Luse {
2148fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
2158fbbfccdSPaul Luse 
2168fbbfccdSPaul Luse 	if (bserrno) {
2178fbbfccdSPaul Luse 		printf("Error %d unloading the bobstore\n", bserrno);
2188fbbfccdSPaul Luse 		cli_context->rc = bserrno;
2198fbbfccdSPaul Luse 	}
2208fbbfccdSPaul Luse 
221c5e63c71SPaul Luse 	/*
222c5e63c71SPaul Luse 	 * Quit if we're in cmd mode or exiting shell mode, otherwise
223c5e63c71SPaul Luse 	 * clear the action field and start the main function again.
224c5e63c71SPaul Luse 	 */
225c5e63c71SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_CMD ||
226c5e63c71SPaul Luse 	    cli_context->action == CLI_SHELL_EXIT) {
2278fbbfccdSPaul Luse 		spdk_app_stop(cli_context->rc);
228c5e63c71SPaul Luse 	} else {
229d4e565b4SPaul Luse 		/* when action is CLI_NONE, we know we need to remain in the shell */
230d4e565b4SPaul Luse 		cli_context->bs = NULL;
231c5e63c71SPaul Luse 		cli_context->action = CLI_NONE;
232c5e63c71SPaul Luse 		cli_start(cli_context, NULL);
233c5e63c71SPaul Luse 	}
2348fbbfccdSPaul Luse }
2358fbbfccdSPaul Luse 
2368fbbfccdSPaul Luse /*
2378fbbfccdSPaul Luse  * Unload the blobstore.
2388fbbfccdSPaul Luse  */
2398fbbfccdSPaul Luse static void
2408fbbfccdSPaul Luse unload_bs(struct cli_context_t *cli_context, char *msg, int bserrno)
2418fbbfccdSPaul Luse {
2428fbbfccdSPaul Luse 	if (bserrno) {
2438fbbfccdSPaul Luse 		printf("%s (err %d)\n", msg, bserrno);
2448fbbfccdSPaul Luse 		cli_context->rc = bserrno;
2458fbbfccdSPaul Luse 	}
246c5e63c71SPaul Luse 
2478fbbfccdSPaul Luse 	if (cli_context->bs) {
248c5e63c71SPaul Luse 		if (cli_context->channel) {
249c5e63c71SPaul Luse 			spdk_bs_free_io_channel(cli_context->channel);
250d4e565b4SPaul Luse 			cli_context->channel = NULL;
251c5e63c71SPaul Luse 		}
2528fbbfccdSPaul Luse 		spdk_bs_unload(cli_context->bs, unload_complete, cli_context);
253a23fa64cSPaul Luse 	} else if (cli_context->cli_mode != CLI_MODE_SCRIPT) {
2548fbbfccdSPaul Luse 		spdk_app_stop(bserrno);
255a23fa64cSPaul Luse 
2568fbbfccdSPaul Luse 	}
2578fbbfccdSPaul Luse }
2588fbbfccdSPaul Luse 
2598fbbfccdSPaul Luse /*
2608fbbfccdSPaul Luse  * Callback for closing a blob.
2618fbbfccdSPaul Luse  */
2628fbbfccdSPaul Luse static void
2638fbbfccdSPaul Luse close_cb(void *arg1, int bserrno)
2648fbbfccdSPaul Luse {
2658fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
2668fbbfccdSPaul Luse 
2678fbbfccdSPaul Luse 	if (bserrno) {
2688fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in close callback",
2698fbbfccdSPaul Luse 			  bserrno);
2708fbbfccdSPaul Luse 		return;
2718fbbfccdSPaul Luse 	}
2728fbbfccdSPaul Luse 	unload_bs(cli_context, "", 0);
2738fbbfccdSPaul Luse }
2748fbbfccdSPaul Luse 
2758fbbfccdSPaul Luse /*
2768fbbfccdSPaul Luse  * Callback function for sync'ing metadata.
2778fbbfccdSPaul Luse  */
2788fbbfccdSPaul Luse static void
279866f15b3SPaul Luse sync_cb(void *arg1, int bserrno)
2808fbbfccdSPaul Luse {
2818fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
2828fbbfccdSPaul Luse 
2838fbbfccdSPaul Luse 	if (bserrno) {
2848fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in sync callback",
2858fbbfccdSPaul Luse 			  bserrno);
2868fbbfccdSPaul Luse 		return;
2878fbbfccdSPaul Luse 	}
2888fbbfccdSPaul Luse 
289e734bb9fSJim Harris 	spdk_blob_close(cli_context->blob, close_cb, cli_context);
2908fbbfccdSPaul Luse }
2918fbbfccdSPaul Luse 
2928fbbfccdSPaul Luse /*
2938fbbfccdSPaul Luse  * Callback function for opening a blob after creating.
2948fbbfccdSPaul Luse  */
2958fbbfccdSPaul Luse static void
296866f15b3SPaul Luse open_now_resize_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
2978fbbfccdSPaul Luse {
2988fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
2998fbbfccdSPaul Luse 	int rc = 0;
3008fbbfccdSPaul Luse 	uint64_t total = 0;
3018fbbfccdSPaul Luse 
3028fbbfccdSPaul Luse 	if (bserrno) {
3038fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in open completion",
3048fbbfccdSPaul Luse 			  bserrno);
3058fbbfccdSPaul Luse 		return;
3068fbbfccdSPaul Luse 	}
3078fbbfccdSPaul Luse 	cli_context->blob = blob;
3088fbbfccdSPaul Luse 
3092c3591f1SJim Harris 	rc = spdk_blob_resize(cli_context->blob, cli_context->num_clusters);
3108fbbfccdSPaul Luse 	if (rc) {
3118fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in blob resize",
3128fbbfccdSPaul Luse 			  bserrno);
3138fbbfccdSPaul Luse 		return;
3148fbbfccdSPaul Luse 	}
3158fbbfccdSPaul Luse 
3168fbbfccdSPaul Luse 	total = spdk_blob_get_num_clusters(cli_context->blob);
3178fbbfccdSPaul Luse 	printf("blob now has USED clusters of %" PRIu64 "\n",
3188fbbfccdSPaul Luse 	       total);
3198fbbfccdSPaul Luse 
3208fbbfccdSPaul Luse 	/*
3218fbbfccdSPaul Luse 	 * Always a good idea to sync after MD changes or the changes
3228fbbfccdSPaul Luse 	 * may be lost if things aren't closed cleanly.
3238fbbfccdSPaul Luse 	 */
3242c3591f1SJim Harris 	spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context);
3258fbbfccdSPaul Luse }
3268fbbfccdSPaul Luse 
3278fbbfccdSPaul Luse /*
3288fbbfccdSPaul Luse  * Callback function for creating a blob.
3298fbbfccdSPaul Luse  */
3308fbbfccdSPaul Luse static void
331866f15b3SPaul Luse blob_create_cb(void *arg1, spdk_blob_id blobid, int bserrno)
3328fbbfccdSPaul Luse {
3338fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
3348fbbfccdSPaul Luse 
3358fbbfccdSPaul Luse 	if (bserrno) {
3368fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in blob create callback",
3378fbbfccdSPaul Luse 			  bserrno);
3388fbbfccdSPaul Luse 		return;
3398fbbfccdSPaul Luse 	}
3408fbbfccdSPaul Luse 
3418fbbfccdSPaul Luse 	cli_context->blobid = blobid;
3428fbbfccdSPaul Luse 	printf("New blob id %" PRIu64 "\n", cli_context->blobid);
3438fbbfccdSPaul Luse 
344e5044f79SPaul Luse 	/* if we're in script mode, we need info on all blobids for later */
345e5044f79SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
346e5044f79SPaul Luse 		g_script.blobid[g_script.blobid_idx++] = blobid;
347e5044f79SPaul Luse 	}
348e5044f79SPaul Luse 
3498fbbfccdSPaul Luse 	/* We have to open the blob before we can do things like resize. */
350d52dbda2SJim Harris 	spdk_bs_open_blob(cli_context->bs, cli_context->blobid,
351866f15b3SPaul Luse 			  open_now_resize_cb, cli_context);
3528fbbfccdSPaul Luse }
3538fbbfccdSPaul Luse 
3548fbbfccdSPaul Luse /*
3558fbbfccdSPaul Luse  * Callback for get_super where we'll continue on to show blobstore info.
3568fbbfccdSPaul Luse  */
3578fbbfccdSPaul Luse static void
358866f15b3SPaul Luse show_bs_cb(void *arg1, spdk_blob_id blobid, int bserrno)
3598fbbfccdSPaul Luse {
3608fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
361b3781b22SDaniel Verkamp 	struct spdk_bs_type bstype;
3628fbbfccdSPaul Luse 	uint64_t val;
3638fbbfccdSPaul Luse 	struct spdk_bdev *bdev = NULL;
3648fbbfccdSPaul Luse 
3658fbbfccdSPaul Luse 	if (bserrno && bserrno != -ENOENT) {
3668fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in get_super callback",
3678fbbfccdSPaul Luse 			  bserrno);
3688fbbfccdSPaul Luse 		return;
3698fbbfccdSPaul Luse 	}
3708fbbfccdSPaul Luse 	cli_context->superid = blobid;
3718fbbfccdSPaul Luse 
3728fbbfccdSPaul Luse 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
3738fbbfccdSPaul Luse 	if (bdev == NULL) {
3748fbbfccdSPaul Luse 		unload_bs(cli_context, "Error w/bdev in get_super callback",
3758fbbfccdSPaul Luse 			  bserrno);
3768fbbfccdSPaul Luse 		return;
3778fbbfccdSPaul Luse 	}
3788fbbfccdSPaul Luse 
3798fbbfccdSPaul Luse 	printf("Blobstore Public Info:\n");
3808fbbfccdSPaul Luse 	printf("\tUsing bdev Product Name: %s\n",
3818fbbfccdSPaul Luse 	       spdk_bdev_get_product_name(bdev));
3828fbbfccdSPaul Luse 	printf("\tAPI Version: %d\n", SPDK_BS_VERSION);
3838fbbfccdSPaul Luse 
3848fbbfccdSPaul Luse 	if (bserrno != -ENOENT) {
3858fbbfccdSPaul Luse 		printf("\tsuper blob ID: %" PRIu64 "\n", cli_context->superid);
3868fbbfccdSPaul Luse 	} else {
3878fbbfccdSPaul Luse 		printf("\tsuper blob ID: none assigned\n");
3888fbbfccdSPaul Luse 	}
3898fbbfccdSPaul Luse 
390866f15b3SPaul Luse 	printf("\tpage size: %" PRIu64 "\n", cli_context->page_size);
3918fbbfccdSPaul Luse 
3928fbbfccdSPaul Luse 	val = spdk_bs_get_cluster_size(cli_context->bs);
3938fbbfccdSPaul Luse 	printf("\tcluster size: %" PRIu64 "\n", val);
3948fbbfccdSPaul Luse 
3958fbbfccdSPaul Luse 	val = spdk_bs_free_cluster_count(cli_context->bs);
3968fbbfccdSPaul Luse 	printf("\t# free clusters: %" PRIu64 "\n", val);
3978fbbfccdSPaul Luse 
398b3781b22SDaniel Verkamp 	bstype = spdk_bs_get_bstype(cli_context->bs);
399b3781b22SDaniel Verkamp 	spdk_trace_dump(stdout, "\tblobstore type:", &bstype, sizeof(bstype));
400b3781b22SDaniel Verkamp 
4018fbbfccdSPaul Luse 	/*
4028fbbfccdSPaul Luse 	 * Private info isn't accessible via the public API but
4038fbbfccdSPaul Luse 	 * may be useful for debug of blobstore based applications.
4048fbbfccdSPaul Luse 	 */
4058fbbfccdSPaul Luse 	printf("\nBlobstore Private Info:\n");
4068fbbfccdSPaul Luse 	printf("\tMetadata start (pages): %" PRIu64 "\n",
4078fbbfccdSPaul Luse 	       cli_context->bs->md_start);
4088fbbfccdSPaul Luse 	printf("\tMetadata length (pages): %d\n",
4098fbbfccdSPaul Luse 	       cli_context->bs->md_len);
4108fbbfccdSPaul Luse 
4118fbbfccdSPaul Luse 	unload_bs(cli_context, "", 0);
4128fbbfccdSPaul Luse }
4138fbbfccdSPaul Luse 
4148fbbfccdSPaul Luse /*
4158fbbfccdSPaul Luse  * Show detailed info about a particular blob.
4168fbbfccdSPaul Luse  */
4178fbbfccdSPaul Luse static void
4188fbbfccdSPaul Luse show_blob(struct cli_context_t *cli_context)
4198fbbfccdSPaul Luse {
4208fbbfccdSPaul Luse 	uint64_t val;
4218fbbfccdSPaul Luse 	struct spdk_xattr_names *names;
4228fbbfccdSPaul Luse 	const void *value;
4238fbbfccdSPaul Luse 	size_t value_len;
424866f15b3SPaul Luse 	char data[BUFSIZE];
4258fbbfccdSPaul Luse 	unsigned int i;
4268fbbfccdSPaul Luse 
4278fbbfccdSPaul Luse 	printf("Blob Public Info:\n");
4288fbbfccdSPaul Luse 
4298fbbfccdSPaul Luse 	printf("blob ID: %" PRIu64 "\n", cli_context->blobid);
4308fbbfccdSPaul Luse 
4318fbbfccdSPaul Luse 	val = spdk_blob_get_num_clusters(cli_context->blob);
4328fbbfccdSPaul Luse 	printf("# of clusters: %" PRIu64 "\n", val);
4338fbbfccdSPaul Luse 
4348fbbfccdSPaul Luse 	printf("# of bytes: %" PRIu64 "\n",
4358fbbfccdSPaul Luse 	       val * spdk_bs_get_cluster_size(cli_context->bs));
4368fbbfccdSPaul Luse 
4378fbbfccdSPaul Luse 	val = spdk_blob_get_num_pages(cli_context->blob);
4388fbbfccdSPaul Luse 	printf("# of pages: %" PRIu64 "\n", val);
4398fbbfccdSPaul Luse 
4402c3591f1SJim Harris 	spdk_blob_get_xattr_names(cli_context->blob, &names);
4418fbbfccdSPaul Luse 
4428fbbfccdSPaul Luse 	printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names));
4438fbbfccdSPaul Luse 	printf("xattrs:\n");
4448fbbfccdSPaul Luse 	for (i = 0; i < spdk_xattr_names_get_count(names); i++) {
4452c3591f1SJim Harris 		spdk_blob_get_xattr_value(cli_context->blob,
4468fbbfccdSPaul Luse 					  spdk_xattr_names_get_name(names, i),
4478fbbfccdSPaul Luse 					  &value, &value_len);
4488fbbfccdSPaul Luse 		if ((value_len + 1) > sizeof(data)) {
4498fbbfccdSPaul Luse 			printf("FYI: adjusting size of xattr due to CLI limits.\n");
4508fbbfccdSPaul Luse 			value_len = sizeof(data) - 1;
4518fbbfccdSPaul Luse 		}
4528fbbfccdSPaul Luse 		memcpy(&data, value, value_len);
4538fbbfccdSPaul Luse 		data[value_len] = '\0';
4548fbbfccdSPaul Luse 		printf("\n(%d) Name:%s\n", i,
4558fbbfccdSPaul Luse 		       spdk_xattr_names_get_name(names, i));
4568fbbfccdSPaul Luse 		printf("(%d) Value:\n", i);
4578fbbfccdSPaul Luse 		spdk_trace_dump(stdout, "", value, value_len);
4588fbbfccdSPaul Luse 	}
4598fbbfccdSPaul Luse 
4608fbbfccdSPaul Luse 	/*
4618fbbfccdSPaul Luse 	 * Private info isn't accessible via the public API but
4628fbbfccdSPaul Luse 	 * may be useful for debug of blobstore based applications.
4638fbbfccdSPaul Luse 	 */
4648fbbfccdSPaul Luse 	printf("\nBlob Private Info:\n");
465*c8efd8a8SJim Harris 	switch (cli_context->blob->state) {
4668fbbfccdSPaul Luse 	case SPDK_BLOB_STATE_DIRTY:
4678fbbfccdSPaul Luse 		printf("state: DIRTY\n");
4688fbbfccdSPaul Luse 		break;
4698fbbfccdSPaul Luse 	case SPDK_BLOB_STATE_CLEAN:
4708fbbfccdSPaul Luse 		printf("state: CLEAN\n");
4718fbbfccdSPaul Luse 		break;
4728fbbfccdSPaul Luse 	case SPDK_BLOB_STATE_LOADING:
4738fbbfccdSPaul Luse 		printf("state: LOADING\n");
4748fbbfccdSPaul Luse 		break;
4758fbbfccdSPaul Luse 	case SPDK_BLOB_STATE_SYNCING:
4768fbbfccdSPaul Luse 		printf("state: SYNCING\n");
4778fbbfccdSPaul Luse 		break;
4788fbbfccdSPaul Luse 	default:
4798fbbfccdSPaul Luse 		printf("state: UNKNOWN\n");
4808fbbfccdSPaul Luse 		break;
4818fbbfccdSPaul Luse 	}
4828fbbfccdSPaul Luse 	printf("open ref count: %d\n",
483*c8efd8a8SJim Harris 	       cli_context->blob->open_ref);
484c5e63c71SPaul Luse 
485c5e63c71SPaul Luse 	spdk_xattr_names_free(names);
4868fbbfccdSPaul Luse }
4878fbbfccdSPaul Luse 
4888fbbfccdSPaul Luse /*
489866f15b3SPaul Luse  * Callback for getting the first blob, shared with simple blob listing as well.
4908fbbfccdSPaul Luse  */
4918fbbfccdSPaul Luse static void
4928fbbfccdSPaul Luse blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno)
4938fbbfccdSPaul Luse {
4948fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
4958fbbfccdSPaul Luse 
4968fbbfccdSPaul Luse 	if (bserrno) {
4978fbbfccdSPaul Luse 		if (bserrno == -ENOENT) {
4988fbbfccdSPaul Luse 			/* this simply means there are no more blobs */
4998fbbfccdSPaul Luse 			unload_bs(cli_context, "", 0);
5008fbbfccdSPaul Luse 		} else {
5018fbbfccdSPaul Luse 			unload_bs(cli_context, "Error in blob iter callback",
5028fbbfccdSPaul Luse 				  bserrno);
5038fbbfccdSPaul Luse 		}
5048fbbfccdSPaul Luse 		return;
5058fbbfccdSPaul Luse 	}
5068fbbfccdSPaul Luse 
5078fbbfccdSPaul Luse 	if (cli_context->action == CLI_LIST_BLOBS) {
508866f15b3SPaul Luse 		printf("\nList BLOBS:\n");
5098fbbfccdSPaul Luse 		printf("Found blob with ID# %" PRIu64 "\n",
5108fbbfccdSPaul Luse 		       spdk_blob_get_id(blob));
5118fbbfccdSPaul Luse 	} else if (spdk_blob_get_id(blob) == cli_context->blobid) {
5128fbbfccdSPaul Luse 		/*
5138fbbfccdSPaul Luse 		 * Found the blob we're looking for, but we need to finish
5148fbbfccdSPaul Luse 		 * iterating even after showing the info so that internally
5158fbbfccdSPaul Luse 		 * the blobstore logic will close the blob. Or we could
5168fbbfccdSPaul Luse 		 * chose to close it now, either way.
5178fbbfccdSPaul Luse 		 */
5188fbbfccdSPaul Luse 		cli_context->blob = blob;
5198fbbfccdSPaul Luse 		show_blob(cli_context);
5208fbbfccdSPaul Luse 	}
5218fbbfccdSPaul Luse 
522ae5a01ddSJim Harris 	spdk_bs_iter_next(cli_context->bs, blob, blob_iter_cb, cli_context);
5238fbbfccdSPaul Luse }
5248fbbfccdSPaul Luse 
5258fbbfccdSPaul Luse /*
5268fbbfccdSPaul Luse  * Callback for setting the super blob ID.
5278fbbfccdSPaul Luse  */
5288fbbfccdSPaul Luse static void
5298fbbfccdSPaul Luse set_super_cb(void *arg1, int bserrno)
5308fbbfccdSPaul Luse {
5318fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
5328fbbfccdSPaul Luse 
5338fbbfccdSPaul Luse 	if (bserrno) {
5348fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in set_super callback",
5358fbbfccdSPaul Luse 			  bserrno);
5368fbbfccdSPaul Luse 		return;
5378fbbfccdSPaul Luse 	}
5388fbbfccdSPaul Luse 
5398fbbfccdSPaul Luse 	printf("Super Blob ID has been set.\n");
5408fbbfccdSPaul Luse 	unload_bs(cli_context, "", 0);
5418fbbfccdSPaul Luse }
5428fbbfccdSPaul Luse 
5438fbbfccdSPaul Luse /*
5448fbbfccdSPaul Luse  * Callback for set_xattr_open where we set or delete xattrs.
5458fbbfccdSPaul Luse  */
5468fbbfccdSPaul Luse static void
547866f15b3SPaul Luse set_xattr_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
5488fbbfccdSPaul Luse {
5498fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
5508fbbfccdSPaul Luse 
5518fbbfccdSPaul Luse 	if (bserrno) {
5528fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in blob open callback",
5538fbbfccdSPaul Luse 			  bserrno);
5548fbbfccdSPaul Luse 		return;
5558fbbfccdSPaul Luse 	}
5568fbbfccdSPaul Luse 	cli_context->blob = blob;
5578fbbfccdSPaul Luse 
5588fbbfccdSPaul Luse 	if (cli_context->action == CLI_SET_XATTR) {
5592c3591f1SJim Harris 		spdk_blob_set_xattr(cli_context->blob, cli_context->key,
5602c3591f1SJim Harris 				    cli_context->value, strlen(cli_context->value) + 1);
5618fbbfccdSPaul Luse 		printf("Xattr has been set.\n");
5628fbbfccdSPaul Luse 	} else {
5632c3591f1SJim Harris 		spdk_blob_remove_xattr(cli_context->blob, cli_context->key);
5648fbbfccdSPaul Luse 		printf("Xattr has been removed.\n");
5658fbbfccdSPaul Luse 	}
5668fbbfccdSPaul Luse 
5672c3591f1SJim Harris 	spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context);
5688fbbfccdSPaul Luse }
5698fbbfccdSPaul Luse 
5708fbbfccdSPaul Luse /*
5718fbbfccdSPaul Luse  * Callback function for reading a blob for dumping to a file.
5728fbbfccdSPaul Luse  */
5738fbbfccdSPaul Luse static void
574866f15b3SPaul Luse read_dump_cb(void *arg1, int bserrno)
5758fbbfccdSPaul Luse {
5768fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
5778fbbfccdSPaul Luse 	uint64_t bytes_written;
5788fbbfccdSPaul Luse 
5798fbbfccdSPaul Luse 	if (bserrno) {
5808fbbfccdSPaul Luse 		fclose(cli_context->fp);
5818fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in read completion",
5828fbbfccdSPaul Luse 			  bserrno);
5838fbbfccdSPaul Luse 		return;
5848fbbfccdSPaul Luse 	}
5858fbbfccdSPaul Luse 
586866f15b3SPaul Luse 	bytes_written = fwrite(cli_context->buff, NUM_PAGES, cli_context->page_size,
5878fbbfccdSPaul Luse 			       cli_context->fp);
5888fbbfccdSPaul Luse 	if (bytes_written != cli_context->page_size) {
5898fbbfccdSPaul Luse 		fclose(cli_context->fp);
5908fbbfccdSPaul Luse 		unload_bs(cli_context, "Error with fwrite",
5918fbbfccdSPaul Luse 			  bserrno);
5928fbbfccdSPaul Luse 		return;
5938fbbfccdSPaul Luse 	}
5948fbbfccdSPaul Luse 
5958fbbfccdSPaul Luse 	printf(".");
5968fbbfccdSPaul Luse 	if (++cli_context->page_count < cli_context->blob_pages) {
5978fbbfccdSPaul Luse 		/* perform another read */
5988fbbfccdSPaul Luse 		spdk_bs_io_read_blob(cli_context->blob, cli_context->channel,
5998fbbfccdSPaul Luse 				     cli_context->buff, cli_context->page_count,
600866f15b3SPaul Luse 				     NUM_PAGES, read_dump_cb, cli_context);
6018fbbfccdSPaul Luse 	} else {
6028fbbfccdSPaul Luse 		/* done reading */
603e9a50eb8SPaul Luse 		printf("\nFile write complete (to %s).\n", cli_context->file);
6048fbbfccdSPaul Luse 		fclose(cli_context->fp);
605e734bb9fSJim Harris 		spdk_blob_close(cli_context->blob, close_cb, cli_context);
6068fbbfccdSPaul Luse 	}
6078fbbfccdSPaul Luse }
6088fbbfccdSPaul Luse 
6098fbbfccdSPaul Luse /*
6108fbbfccdSPaul Luse  * Callback for write completion on the import of a file to a blob.
6118fbbfccdSPaul Luse  */
6128fbbfccdSPaul Luse static void
613866f15b3SPaul Luse write_imp_cb(void *arg1, int bserrno)
6148fbbfccdSPaul Luse {
6158fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
6168fbbfccdSPaul Luse 	uint64_t bytes_read;
6178fbbfccdSPaul Luse 
6188fbbfccdSPaul Luse 	if (bserrno) {
6198fbbfccdSPaul Luse 		fclose(cli_context->fp);
6208fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in write completion",
6218fbbfccdSPaul Luse 			  bserrno);
6228fbbfccdSPaul Luse 		return;
6238fbbfccdSPaul Luse 	}
6248fbbfccdSPaul Luse 
6258fbbfccdSPaul Luse 	if (cli_context->bytes_so_far < cli_context->filesize) {
6268fbbfccdSPaul Luse 		/* perform another file read */
6278fbbfccdSPaul Luse 		bytes_read = fread(cli_context->buff, 1,
6288fbbfccdSPaul Luse 				   cli_context->page_size,
6298fbbfccdSPaul Luse 				   cli_context->fp);
6308fbbfccdSPaul Luse 		cli_context->bytes_so_far += bytes_read;
6318fbbfccdSPaul Luse 
6328fbbfccdSPaul Luse 		/* if this read is < 1 page, fill with 0s */
6338fbbfccdSPaul Luse 		if (bytes_read < cli_context->page_size) {
6348fbbfccdSPaul Luse 			uint8_t *offset = cli_context->buff + bytes_read;
635866f15b3SPaul Luse 			memset(offset, 0, cli_context->page_size - bytes_read);
6368fbbfccdSPaul Luse 		}
6378fbbfccdSPaul Luse 	} else {
6388fbbfccdSPaul Luse 		/*
6398fbbfccdSPaul Luse 		 * Done reading the file, fill the rest of the blob with 0s,
6408fbbfccdSPaul Luse 		 * yeah we're memsetting the same page over and over here
6418fbbfccdSPaul Luse 		 */
6428fbbfccdSPaul Luse 		memset(cli_context->buff, 0, cli_context->page_size);
6438fbbfccdSPaul Luse 	}
6448fbbfccdSPaul Luse 	if (++cli_context->page_count < cli_context->blob_pages) {
6458fbbfccdSPaul Luse 		printf(".");
6468fbbfccdSPaul Luse 		spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
6478fbbfccdSPaul Luse 				      cli_context->buff, cli_context->page_count,
648866f15b3SPaul Luse 				      NUM_PAGES, write_imp_cb, cli_context);
6498fbbfccdSPaul Luse 	} else {
6508fbbfccdSPaul Luse 		/* done writing */
651e9a50eb8SPaul Luse 		printf("\nBlob import complete (from %s).\n", cli_context->file);
6528fbbfccdSPaul Luse 		fclose(cli_context->fp);
653e734bb9fSJim Harris 		spdk_blob_close(cli_context->blob, close_cb, cli_context);
6548fbbfccdSPaul Luse 	}
6558fbbfccdSPaul Luse }
6568fbbfccdSPaul Luse 
6578fbbfccdSPaul Luse /*
6588fbbfccdSPaul Luse  * Callback for open blobs where we'll continue on dump a blob to a file or
6598fbbfccdSPaul Luse  * import a file to a blob. For dump, the resulting file will always be the
6608fbbfccdSPaul Luse  * full size of the blob.  For import, the blob will fill with the file
6618fbbfccdSPaul Luse  * contents first and then 0 out the rest of the blob.
6628fbbfccdSPaul Luse  */
6638fbbfccdSPaul Luse static void
664866f15b3SPaul Luse dump_imp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
6658fbbfccdSPaul Luse {
6668fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
6678fbbfccdSPaul Luse 
6688fbbfccdSPaul Luse 	if (bserrno) {
6698fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in blob open callback",
6708fbbfccdSPaul Luse 			  bserrno);
6718fbbfccdSPaul Luse 		return;
6728fbbfccdSPaul Luse 	}
6738fbbfccdSPaul Luse 	cli_context->blob = blob;
6748fbbfccdSPaul Luse 
6758fbbfccdSPaul Luse 	/*
6768fbbfccdSPaul Luse 	 * We'll transfer just one page at a time to keep the buffer
6778fbbfccdSPaul Luse 	 * small. This could be bigger of course.
6788fbbfccdSPaul Luse 	 */
6798fbbfccdSPaul Luse 	cli_context->buff = spdk_dma_malloc(cli_context->page_size,
680866f15b3SPaul Luse 					    ALIGN_4K, NULL);
6818fbbfccdSPaul Luse 	if (cli_context->buff == NULL) {
682809dc418SPaul Luse 		printf("Error in allocating memory\n");
683e734bb9fSJim Harris 		spdk_blob_close(cli_context->blob, close_cb, cli_context);
6848fbbfccdSPaul Luse 		return;
6858fbbfccdSPaul Luse 	}
6868fbbfccdSPaul Luse 	printf("Working");
6878fbbfccdSPaul Luse 	cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob);
6888fbbfccdSPaul Luse 	cli_context->page_count = 0;
6894952f254SDaniel Verkamp 	if (cli_context->action == CLI_DUMP_BLOB) {
6908fbbfccdSPaul Luse 		cli_context->fp = fopen(cli_context->file, "w");
691809dc418SPaul Luse 		if (cli_context->fp == NULL) {
692809dc418SPaul Luse 			printf("Error in opening file\n");
693e734bb9fSJim Harris 			spdk_blob_close(cli_context->blob, close_cb, cli_context);
694809dc418SPaul Luse 			return;
695809dc418SPaul Luse 		}
6968fbbfccdSPaul Luse 
6978fbbfccdSPaul Luse 		/* read a page of data from the blob */
6988fbbfccdSPaul Luse 		spdk_bs_io_read_blob(cli_context->blob, cli_context->channel,
6998fbbfccdSPaul Luse 				     cli_context->buff, cli_context->page_count,
700866f15b3SPaul Luse 				     NUM_PAGES, read_dump_cb, cli_context);
7018fbbfccdSPaul Luse 	} else {
7028fbbfccdSPaul Luse 		cli_context->fp = fopen(cli_context->file, "r");
703809dc418SPaul Luse 		if (cli_context->fp == NULL) {
704a0e22e13Spaul luse 			printf("Error in opening file: errno %d\n", errno);
705e734bb9fSJim Harris 			spdk_blob_close(cli_context->blob, close_cb, cli_context);
706809dc418SPaul Luse 			return;
707809dc418SPaul Luse 		}
7088fbbfccdSPaul Luse 
7098fbbfccdSPaul Luse 		/* get the filesize then rewind read a page of data from file */
7108fbbfccdSPaul Luse 		fseek(cli_context->fp, 0L, SEEK_END);
7118fbbfccdSPaul Luse 		cli_context->filesize = ftell(cli_context->fp);
7128fbbfccdSPaul Luse 		rewind(cli_context->fp);
713866f15b3SPaul Luse 		cli_context->bytes_so_far = fread(cli_context->buff, NUM_PAGES,
7148fbbfccdSPaul Luse 						  cli_context->page_size,
7158fbbfccdSPaul Luse 						  cli_context->fp);
7168fbbfccdSPaul Luse 
7178fbbfccdSPaul Luse 		/* if the file is < a page, fill the rest with 0s */
7188fbbfccdSPaul Luse 		if (cli_context->filesize < cli_context->page_size) {
7198fbbfccdSPaul Luse 			uint8_t *offset =
7208fbbfccdSPaul Luse 				cli_context->buff + cli_context->filesize;
7218fbbfccdSPaul Luse 
7228fbbfccdSPaul Luse 			memset(offset, 0,
7238fbbfccdSPaul Luse 			       cli_context->page_size - cli_context->filesize);
7248fbbfccdSPaul Luse 		}
7258fbbfccdSPaul Luse 
7268fbbfccdSPaul Luse 		spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
7278fbbfccdSPaul Luse 				      cli_context->buff, cli_context->page_count,
728866f15b3SPaul Luse 				      NUM_PAGES, write_imp_cb, cli_context);
7298fbbfccdSPaul Luse 	}
7308fbbfccdSPaul Luse }
7318fbbfccdSPaul Luse 
7328fbbfccdSPaul Luse /*
7338fbbfccdSPaul Luse  * Callback function for writing a specific pattern to page 0.
7348fbbfccdSPaul Luse  */
7358fbbfccdSPaul Luse static void
736866f15b3SPaul Luse write_cb(void *arg1, int bserrno)
7378fbbfccdSPaul Luse {
7388fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
7398fbbfccdSPaul Luse 
7408fbbfccdSPaul Luse 	if (bserrno) {
7418fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in write completion",
7428fbbfccdSPaul Luse 			  bserrno);
7438fbbfccdSPaul Luse 		return;
7448fbbfccdSPaul Luse 	}
7458fbbfccdSPaul Luse 	printf(".");
7468fbbfccdSPaul Luse 	if (++cli_context->page_count < cli_context->blob_pages) {
7478fbbfccdSPaul Luse 		spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
7488fbbfccdSPaul Luse 				      cli_context->buff, cli_context->page_count,
749866f15b3SPaul Luse 				      NUM_PAGES, write_cb, cli_context);
7508fbbfccdSPaul Luse 	} else {
7518fbbfccdSPaul Luse 		/* done writing */
752e9a50eb8SPaul Luse 		printf("\nBlob fill complete (with 0x%x).\n", cli_context->fill_value);
753e734bb9fSJim Harris 		spdk_blob_close(cli_context->blob, close_cb, cli_context);
7548fbbfccdSPaul Luse 	}
7558fbbfccdSPaul Luse }
7568fbbfccdSPaul Luse 
7578fbbfccdSPaul Luse /*
758866f15b3SPaul Luse  * Callback function to fill a blob with a value, callback from open.
7598fbbfccdSPaul Luse  */
7608fbbfccdSPaul Luse static void
761866f15b3SPaul Luse fill_blob_cb(void *arg1, struct spdk_blob *blob, int bserrno)
7628fbbfccdSPaul Luse {
763866f15b3SPaul Luse 	struct cli_context_t *cli_context = arg1;
7648fbbfccdSPaul Luse 
7658fbbfccdSPaul Luse 	if (bserrno) {
766866f15b3SPaul Luse 		unload_bs(cli_context, "Error in open callback",
7678fbbfccdSPaul Luse 			  bserrno);
7688fbbfccdSPaul Luse 		return;
7698fbbfccdSPaul Luse 	}
770866f15b3SPaul Luse 
7718fbbfccdSPaul Luse 	cli_context->blob = blob;
7728fbbfccdSPaul Luse 	cli_context->page_count = 0;
7738fbbfccdSPaul Luse 	cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob);
7748fbbfccdSPaul Luse 	cli_context->buff = spdk_dma_malloc(cli_context->page_size,
775866f15b3SPaul Luse 					    ALIGN_4K, NULL);
7768fbbfccdSPaul Luse 	if (cli_context->buff == NULL) {
7778fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in allocating memory",
7788fbbfccdSPaul Luse 			  -ENOMEM);
7798fbbfccdSPaul Luse 		return;
7808fbbfccdSPaul Luse 	}
7818fbbfccdSPaul Luse 
7828fbbfccdSPaul Luse 	memset(cli_context->buff, cli_context->fill_value,
7838fbbfccdSPaul Luse 	       cli_context->page_size);
784e5044f79SPaul Luse 	printf("Working");
7858fbbfccdSPaul Luse 	spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
7868fbbfccdSPaul Luse 			      cli_context->buff,
787866f15b3SPaul Luse 			      STARTING_PAGE, NUM_PAGES, write_cb, cli_context);
7888fbbfccdSPaul Luse }
7898fbbfccdSPaul Luse 
7908fbbfccdSPaul Luse /*
791866f15b3SPaul Luse  * Multiple actions require us to open the bs first so here we use
792866f15b3SPaul Luse  * a common callback to set a bunch of values and then move on to
793866f15b3SPaul Luse  * the next step saved off via function pointer.
7948fbbfccdSPaul Luse  */
7958fbbfccdSPaul Luse static void
796866f15b3SPaul Luse load_bs_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
7978fbbfccdSPaul Luse {
7988fbbfccdSPaul Luse 	struct cli_context_t *cli_context = arg1;
7998fbbfccdSPaul Luse 
8008fbbfccdSPaul Luse 	if (bserrno) {
8018fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in load callback",
8028fbbfccdSPaul Luse 			  bserrno);
8038fbbfccdSPaul Luse 		return;
8048fbbfccdSPaul Luse 	}
805866f15b3SPaul Luse 
8068fbbfccdSPaul Luse 	cli_context->bs = bs;
8078fbbfccdSPaul Luse 	cli_context->page_size = spdk_bs_get_page_size(cli_context->bs);
8088fbbfccdSPaul Luse 	cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs);
8098fbbfccdSPaul Luse 	if (cli_context->channel == NULL) {
8108fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in allocating channel",
8118fbbfccdSPaul Luse 			  -ENOMEM);
8128fbbfccdSPaul Luse 		return;
8138fbbfccdSPaul Luse 	}
8148fbbfccdSPaul Luse 
815866f15b3SPaul Luse 	switch (cli_context->action) {
816866f15b3SPaul Luse 	case CLI_SET_SUPER:
817866f15b3SPaul Luse 		spdk_bs_set_super(cli_context->bs, cli_context->superid,
818866f15b3SPaul Luse 				  set_super_cb, cli_context);
819866f15b3SPaul Luse 		break;
820866f15b3SPaul Luse 	case CLI_SHOW_BS:
821866f15b3SPaul Luse 		spdk_bs_get_super(cli_context->bs, show_bs_cb, cli_context);
822866f15b3SPaul Luse 		break;
823866f15b3SPaul Luse 	case CLI_CREATE_BLOB:
824d52dbda2SJim Harris 		spdk_bs_create_blob(cli_context->bs, blob_create_cb, cli_context);
825866f15b3SPaul Luse 		break;
826866f15b3SPaul Luse 	case CLI_SET_XATTR:
827866f15b3SPaul Luse 	case CLI_REM_XATTR:
828d52dbda2SJim Harris 		spdk_bs_open_blob(cli_context->bs, cli_context->blobid,
829866f15b3SPaul Luse 				  set_xattr_cb, cli_context);
830866f15b3SPaul Luse 		break;
831866f15b3SPaul Luse 	case CLI_SHOW_BLOB:
832866f15b3SPaul Luse 	case CLI_LIST_BLOBS:
833d52dbda2SJim Harris 		spdk_bs_iter_first(cli_context->bs, blob_iter_cb, cli_context);
834866f15b3SPaul Luse 
835866f15b3SPaul Luse 		break;
8364952f254SDaniel Verkamp 	case CLI_DUMP_BLOB:
8374952f254SDaniel Verkamp 	case CLI_IMPORT_BLOB:
838d52dbda2SJim Harris 		spdk_bs_open_blob(cli_context->bs, cli_context->blobid,
839866f15b3SPaul Luse 				  dump_imp_open_cb, cli_context);
840866f15b3SPaul Luse 		break;
841866f15b3SPaul Luse 	case CLI_FILL:
842d52dbda2SJim Harris 		spdk_bs_open_blob(cli_context->bs, cli_context->blobid,
843866f15b3SPaul Luse 				  fill_blob_cb, cli_context);
844866f15b3SPaul Luse 		break;
845866f15b3SPaul Luse 
846866f15b3SPaul Luse 	default:
847866f15b3SPaul Luse 		/* should never get here */
84898a2ec3eSPaul Luse 		exit(-1);
849866f15b3SPaul Luse 		break;
850866f15b3SPaul Luse 	}
8518fbbfccdSPaul Luse }
8528fbbfccdSPaul Luse 
8538fbbfccdSPaul Luse /*
854866f15b3SPaul Luse  * Load the blobstore.
8558fbbfccdSPaul Luse  */
8568fbbfccdSPaul Luse static void
8578fbbfccdSPaul Luse load_bs(struct cli_context_t *cli_context)
8588fbbfccdSPaul Luse {
8598fbbfccdSPaul Luse 	struct spdk_bdev *bdev = NULL;
8608fbbfccdSPaul Luse 	struct spdk_bs_dev *bs_dev = NULL;
8618fbbfccdSPaul Luse 
8628fbbfccdSPaul Luse 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
8638fbbfccdSPaul Luse 	if (bdev == NULL) {
8648fbbfccdSPaul Luse 		printf("Could not find a bdev\n");
8658fbbfccdSPaul Luse 		spdk_app_stop(-1);
8668fbbfccdSPaul Luse 		return;
8678fbbfccdSPaul Luse 	}
8688fbbfccdSPaul Luse 
869e9cf3ecfSDaniel Verkamp 	bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL);
8708fbbfccdSPaul Luse 	if (bs_dev == NULL) {
8718fbbfccdSPaul Luse 		printf("Could not create blob bdev!!\n");
8728fbbfccdSPaul Luse 		spdk_app_stop(-1);
8738fbbfccdSPaul Luse 		return;
8748fbbfccdSPaul Luse 	}
8758fbbfccdSPaul Luse 
876866f15b3SPaul Luse 	spdk_bs_load(bs_dev, NULL, load_bs_cb, cli_context);
8778fbbfccdSPaul Luse }
8788fbbfccdSPaul Luse 
8798fbbfccdSPaul Luse /*
8808fbbfccdSPaul Luse  * Lists all the blobs on this blobstore.
8818fbbfccdSPaul Luse  */
8828fbbfccdSPaul Luse static void
883c5e63c71SPaul Luse list_bdevs(struct cli_context_t *cli_context)
8848fbbfccdSPaul Luse {
8858fbbfccdSPaul Luse 	struct spdk_bdev *bdev = NULL;
8868fbbfccdSPaul Luse 
8878fbbfccdSPaul Luse 	printf("\nList bdevs:\n");
8888fbbfccdSPaul Luse 
8898fbbfccdSPaul Luse 	bdev = spdk_bdev_first();
8908fbbfccdSPaul Luse 	if (bdev == NULL) {
8918fbbfccdSPaul Luse 		printf("Could not find a bdev\n");
8928fbbfccdSPaul Luse 	}
8938fbbfccdSPaul Luse 	while (bdev) {
8948fbbfccdSPaul Luse 		printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev));
8958fbbfccdSPaul Luse 		printf("\tbdev Product Name: %s\n",
8968fbbfccdSPaul Luse 		       spdk_bdev_get_product_name(bdev));
8978fbbfccdSPaul Luse 		bdev = spdk_bdev_next(bdev);
8988fbbfccdSPaul Luse 	}
8998fbbfccdSPaul Luse 
9008fbbfccdSPaul Luse 	printf("\n");
901c5e63c71SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_CMD) {
9028fbbfccdSPaul Luse 		spdk_app_stop(0);
903c5e63c71SPaul Luse 	} else {
904c5e63c71SPaul Luse 		cli_context->action = CLI_NONE;
905c5e63c71SPaul Luse 		cli_start(cli_context, NULL);
906c5e63c71SPaul Luse 	}
9078fbbfccdSPaul Luse }
9088fbbfccdSPaul Luse 
9098fbbfccdSPaul Luse /*
9108fbbfccdSPaul Luse  * Callback function for initializing a blob.
9118fbbfccdSPaul Luse  */
9128fbbfccdSPaul Luse static void
913866f15b3SPaul Luse bs_init_cb(void *cb_arg, struct spdk_blob_store *bs,
9148fbbfccdSPaul Luse 	   int bserrno)
9158fbbfccdSPaul Luse {
9168fbbfccdSPaul Luse 	struct cli_context_t *cli_context = cb_arg;
9178fbbfccdSPaul Luse 
9188fbbfccdSPaul Luse 	if (bserrno) {
9198fbbfccdSPaul Luse 		unload_bs(cli_context, "Error in bs init callback",
9208fbbfccdSPaul Luse 			  bserrno);
9218fbbfccdSPaul Luse 		return;
9228fbbfccdSPaul Luse 	}
9238fbbfccdSPaul Luse 	cli_context->bs = bs;
9248fbbfccdSPaul Luse 	printf("blobstore init'd: (%p)\n", cli_context->bs);
9258fbbfccdSPaul Luse 
9268fbbfccdSPaul Luse 	unload_bs(cli_context, "", 0);
9278fbbfccdSPaul Luse }
9288fbbfccdSPaul Luse 
9298fbbfccdSPaul Luse /*
9308fbbfccdSPaul Luse  * Initialize a new blobstore.
9318fbbfccdSPaul Luse  */
9328fbbfccdSPaul Luse static void
9338fbbfccdSPaul Luse init_bs(struct cli_context_t *cli_context)
9348fbbfccdSPaul Luse {
9358fbbfccdSPaul Luse 	struct spdk_bdev *bdev = NULL;
9368fbbfccdSPaul Luse 
9378fbbfccdSPaul Luse 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
9388fbbfccdSPaul Luse 	if (bdev == NULL) {
9398fbbfccdSPaul Luse 		printf("Could not find a bdev\n");
9408fbbfccdSPaul Luse 		spdk_app_stop(-1);
9418fbbfccdSPaul Luse 		return;
9428fbbfccdSPaul Luse 	}
943866f15b3SPaul Luse 	printf("Init blobstore using bdev Product Name: %s\n",
9448fbbfccdSPaul Luse 	       spdk_bdev_get_product_name(bdev));
9458fbbfccdSPaul Luse 
946c5e63c71SPaul Luse 	cli_context->bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL);
947c5e63c71SPaul Luse 	if (cli_context->bs_dev == NULL) {
9488fbbfccdSPaul Luse 		printf("Could not create blob bdev!!\n");
9498fbbfccdSPaul Luse 		spdk_app_stop(-1);
9508fbbfccdSPaul Luse 		return;
9518fbbfccdSPaul Luse 	}
9528fbbfccdSPaul Luse 
953866f15b3SPaul Luse 	spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb,
9548fbbfccdSPaul Luse 		     cli_context);
9558fbbfccdSPaul Luse }
9568fbbfccdSPaul Luse 
9578fbbfccdSPaul Luse /*
958c5e63c71SPaul Luse  * Common cmd/option parser for command and shell modes.
959c5e63c71SPaul Luse  */
960c5e63c71SPaul Luse static bool
961c5e63c71SPaul Luse cmd_parser(int argc, char **argv, struct cli_context_t *cli_context)
9628fbbfccdSPaul Luse {
9638fbbfccdSPaul Luse 	int op;
964c5e63c71SPaul Luse 	int cmd_chosen = 0;
965c5e63c71SPaul Luse 	char resp;
9668fbbfccdSPaul Luse 
967cfa4cae0SPaul Luse 	while ((op = getopt(argc, argv, "b:c:d:f:hil:m:n:p:r:s:ST:Xx:")) != -1) {
9688fbbfccdSPaul Luse 		switch (op) {
969cfa4cae0SPaul Luse 		case 'b':
970cfa4cae0SPaul Luse 			if (strcmp(cli_context->bdev_name, "") == 0) {
971cfa4cae0SPaul Luse 				snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg);
972cfa4cae0SPaul Luse 			} else {
973cfa4cae0SPaul Luse 				printf("Current setting for -b is: %s\n", cli_context->bdev_name);
974cfa4cae0SPaul Luse 				usage(cli_context, "ERROR: -b option can only be set once.\n");
975cfa4cae0SPaul Luse 			}
976cfa4cae0SPaul Luse 			break;
9778fbbfccdSPaul Luse 		case 'c':
978c5e63c71SPaul Luse 			if (cli_context->app_started == false) {
979c5e63c71SPaul Luse 				cli_context->config_file = optarg;
980c5e63c71SPaul Luse 			} else {
981e5044f79SPaul Luse 				usage(cli_context, "ERROR: -c option not valid during shell mode.\n");
982c5e63c71SPaul Luse 			}
9838fbbfccdSPaul Luse 			break;
9848fbbfccdSPaul Luse 		case 'd':
985cf0d7736SPaul Luse 			if (argv[optind] != NULL) {
986c5e63c71SPaul Luse 				cmd_chosen++;
9874952f254SDaniel Verkamp 				cli_context->action = CLI_DUMP_BLOB;
9888fbbfccdSPaul Luse 				cli_context->blobid = atoll(optarg);
989e5044f79SPaul Luse 				snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]);
990cf0d7736SPaul Luse 			} else {
991cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
992cf0d7736SPaul Luse 			}
9938fbbfccdSPaul Luse 			break;
9948fbbfccdSPaul Luse 		case 'f':
995cf0d7736SPaul Luse 			if (argv[optind] != NULL) {
996c5e63c71SPaul Luse 				cmd_chosen++;
9978fbbfccdSPaul Luse 				cli_context->action = CLI_FILL;
9988fbbfccdSPaul Luse 				cli_context->blobid = atoll(optarg);
999e5044f79SPaul Luse 				cli_context->fill_value = atoi(argv[optind]);
1000cf0d7736SPaul Luse 			} else {
1001cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
1002cf0d7736SPaul Luse 			}
10038fbbfccdSPaul Luse 			break;
1004c5e63c71SPaul Luse 		case 'h':
1005c5e63c71SPaul Luse 			cmd_chosen++;
1006c5e63c71SPaul Luse 			cli_context->action = CLI_HELP;
1007c5e63c71SPaul Luse 			break;
10088fbbfccdSPaul Luse 		case 'i':
1009e5044f79SPaul Luse 			if (cli_context->cli_mode != CLI_MODE_SCRIPT) {
1010e5044f79SPaul Luse 				printf("Your entire blobstore will be destroyed. Are you sure? (y/n) ");
1011c5e63c71SPaul Luse 				if (scanf("%c%*c", &resp)) {
1012c5e63c71SPaul Luse 					if (resp == 'y' || resp == 'Y') {
1013c5e63c71SPaul Luse 						cmd_chosen++;
10148fbbfccdSPaul Luse 						cli_context->action = CLI_INIT_BS;
1015c5e63c71SPaul Luse 					} else {
1016c5e63c71SPaul Luse 						if (cli_context->cli_mode == CLI_MODE_CMD) {
1017e5044f79SPaul Luse 							spdk_app_stop(0);
1018e5044f79SPaul Luse 							return false;
1019c5e63c71SPaul Luse 						}
1020c5e63c71SPaul Luse 					}
1021c5e63c71SPaul Luse 				}
1022e5044f79SPaul Luse 			} else {
1023e5044f79SPaul Luse 				cmd_chosen++;
1024e5044f79SPaul Luse 				cli_context->action = CLI_INIT_BS;
1025e5044f79SPaul Luse 			}
10268fbbfccdSPaul Luse 			break;
10278fbbfccdSPaul Luse 		case 'r':
1028cf0d7736SPaul Luse 			if (argv[optind] != NULL) {
1029c5e63c71SPaul Luse 				cmd_chosen++;
10308fbbfccdSPaul Luse 				cli_context->action = CLI_REM_XATTR;
10318fbbfccdSPaul Luse 				cli_context->blobid = atoll(optarg);
1032e5044f79SPaul Luse 				snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]);
1033cf0d7736SPaul Luse 			} else {
1034cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
1035cf0d7736SPaul Luse 			}
10368fbbfccdSPaul Luse 			break;
10378fbbfccdSPaul Luse 		case 'l':
10388fbbfccdSPaul Luse 			if (strcmp("bdevs", optarg) == 0) {
1039c5e63c71SPaul Luse 				cmd_chosen++;
10408fbbfccdSPaul Luse 				cli_context->action = CLI_LIST_BDEVS;
10418fbbfccdSPaul Luse 			} else if (strcmp("blobs", optarg) == 0) {
1042c5e63c71SPaul Luse 				cmd_chosen++;
10438fbbfccdSPaul Luse 				cli_context->action = CLI_LIST_BLOBS;
10448fbbfccdSPaul Luse 			} else {
1045e5044f79SPaul Luse 				usage(cli_context, "ERROR: invalid option for list\n");
10468fbbfccdSPaul Luse 			}
10478fbbfccdSPaul Luse 			break;
10488fbbfccdSPaul Luse 		case 'm':
1049cf0d7736SPaul Luse 			if (argv[optind] != NULL) {
1050c5e63c71SPaul Luse 				cmd_chosen++;
10514952f254SDaniel Verkamp 				cli_context->action = CLI_IMPORT_BLOB;
10528fbbfccdSPaul Luse 				cli_context->blobid = atoll(optarg);
1053e5044f79SPaul Luse 				snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]);
1054cf0d7736SPaul Luse 			} else {
1055cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
1056cf0d7736SPaul Luse 			}
10578fbbfccdSPaul Luse 			break;
10588fbbfccdSPaul Luse 		case 'n':
10598fbbfccdSPaul Luse 			cli_context->num_clusters = atoi(optarg);
10608fbbfccdSPaul Luse 			if (cli_context->num_clusters > 0) {
1061cf0d7736SPaul Luse 				cmd_chosen++;
10628fbbfccdSPaul Luse 				cli_context->action = CLI_CREATE_BLOB;
10638fbbfccdSPaul Luse 			} else {
1064e5044f79SPaul Luse 				usage(cli_context, "ERROR: invalid option for new\n");
10658fbbfccdSPaul Luse 			}
10668fbbfccdSPaul Luse 			break;
10678fbbfccdSPaul Luse 		case 'p':
1068c5e63c71SPaul Luse 			cmd_chosen++;
10698fbbfccdSPaul Luse 			cli_context->action = CLI_SET_SUPER;
10708fbbfccdSPaul Luse 			cli_context->superid = atoll(optarg);
10718fbbfccdSPaul Luse 			break;
1072c5e63c71SPaul Luse 		case 'S':
1073c5e63c71SPaul Luse 			if (cli_context->cli_mode == CLI_MODE_CMD) {
1074cf0d7736SPaul Luse 				cmd_chosen++;
1075c5e63c71SPaul Luse 				cli_context->cli_mode = CLI_MODE_SHELL;
1076c5e63c71SPaul Luse 			}
1077c5e63c71SPaul Luse 			cli_context->action = CLI_NONE;
1078c5e63c71SPaul Luse 			break;
10798fbbfccdSPaul Luse 		case 's':
1080c5e63c71SPaul Luse 			cmd_chosen++;
10818fbbfccdSPaul Luse 			if (strcmp("bs", optarg) == 0) {
10828fbbfccdSPaul Luse 				cli_context->action = CLI_SHOW_BS;
10838fbbfccdSPaul Luse 			} else {
10848fbbfccdSPaul Luse 				cli_context->action = CLI_SHOW_BLOB;
10858fbbfccdSPaul Luse 				cli_context->blobid = atoll(optarg);
10868fbbfccdSPaul Luse 			}
10878fbbfccdSPaul Luse 			break;
1088e5044f79SPaul Luse 		case 'T':
1089e5044f79SPaul Luse 			if (cli_context->cli_mode == CLI_MODE_CMD) {
1090e5044f79SPaul Luse 				cmd_chosen++;
1091e5044f79SPaul Luse 				cli_context->cli_mode = CLI_MODE_SCRIPT;
1092e5044f79SPaul Luse 				if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) {
1093e5044f79SPaul Luse 					g_script.ignore_errors = true;
1094e5044f79SPaul Luse 				} else {
1095e5044f79SPaul Luse 					g_script.ignore_errors = false;
1096e5044f79SPaul Luse 				}
1097e5044f79SPaul Luse 				snprintf(cli_context->script_file, BUFSIZE, "%s", optarg);
1098e5044f79SPaul Luse 			} else {
1099e5044f79SPaul Luse 				cli_context->action = CLI_NONE;
1100e5044f79SPaul Luse 			}
1101e5044f79SPaul Luse 			break;
1102c5e63c71SPaul Luse 		case 'X':
1103c5e63c71SPaul Luse 			cmd_chosen++;
1104c5e63c71SPaul Luse 			cli_context->action = CLI_SHELL_EXIT;
1105c5e63c71SPaul Luse 			break;
11068fbbfccdSPaul Luse 		case 'x':
1107cf0d7736SPaul Luse 			if (argv[optind] != NULL || argv[optind + 1] != NULL) {
1108c5e63c71SPaul Luse 				cmd_chosen++;
11098fbbfccdSPaul Luse 				cli_context->action = CLI_SET_XATTR;
11108fbbfccdSPaul Luse 				cli_context->blobid = atoll(optarg);
1111e5044f79SPaul Luse 				snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]);
1112e5044f79SPaul Luse 				snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]);
1113cf0d7736SPaul Luse 			} else {
1114cf0d7736SPaul Luse 				usage(cli_context, "ERROR: missing parameter.\n");
1115cf0d7736SPaul Luse 			}
11168fbbfccdSPaul Luse 			break;
11178fbbfccdSPaul Luse 		default:
1118e5044f79SPaul Luse 			usage(cli_context, "ERROR: invalid option\n");
11198fbbfccdSPaul Luse 		}
1120db26b96fSPaul Luse 		/* only one actual command can be done at a time */
1121db26b96fSPaul Luse 		if (cmd_chosen > 1) {
1122e5044f79SPaul Luse 			usage(cli_context, "Error: Please choose only one command\n");
11238fbbfccdSPaul Luse 		}
11248fbbfccdSPaul Luse 	}
11258fbbfccdSPaul Luse 
1126c5e63c71SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) {
1127e5044f79SPaul Luse 		usage(cli_context, "Error: Please choose a command.\n");
11288fbbfccdSPaul Luse 	}
11298fbbfccdSPaul Luse 
1130cfa4cae0SPaul Luse 	/*
1131cfa4cae0SPaul Luse 	 * We don't check the local boolean because in some modes it will have been set
1132cfa4cae0SPaul Luse 	 * on and earlier command.
1133cfa4cae0SPaul Luse 	 */
1134cfa4cae0SPaul Luse 	if (strcmp(cli_context->bdev_name, "") == 0) {
1135cfa4cae0SPaul Luse 		usage(cli_context, "Error: -b option is required.\n");
1136cfa4cae0SPaul Luse 		cmd_chosen = 0;
1137cfa4cae0SPaul Luse 	}
1138cfa4cae0SPaul Luse 
1139c5e63c71SPaul Luse 	/* in shell mode we'll call getopt multiple times so need to reset its index */
1140c5e63c71SPaul Luse 	optind = 0;
1141db26b96fSPaul Luse 	return (cmd_chosen == 1);
1142c5e63c71SPaul Luse }
1143c5e63c71SPaul Luse 
1144c5e63c71SPaul Luse /*
1145e5044f79SPaul Luse  * In script mode, we parsed a script file at startup and saved off a bunch of cmd
1146e5044f79SPaul Luse  * lines that we now parse with each run of cli_start so we us the same cmd parser
1147e5044f79SPaul Luse  * as cmd and shell modes.
1148e5044f79SPaul Luse  */
1149e5044f79SPaul Luse static bool
1150e5044f79SPaul Luse line_parser(struct cli_context_t *cli_context)
1151e5044f79SPaul Luse {
1152e5044f79SPaul Luse 	bool cmd_chosen;
1153e5044f79SPaul Luse 	char *tok = NULL;
1154e5044f79SPaul Luse 	int blob_num = 0;
1155e5044f79SPaul Luse 	int start_idx = cli_context->argc;
1156e5044f79SPaul Luse 	int i;
1157e5044f79SPaul Luse 
1158e5044f79SPaul Luse 	printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]);
1159e5044f79SPaul Luse 	tok = strtok(g_script.cmdline[g_script.cmdline_idx], " ");
1160e5044f79SPaul Luse 	while (tok != NULL) {
1161e5044f79SPaul Luse 		/*
1162e5044f79SPaul Luse 		 * We support one replaceable token right now, a $Bn
1163e5044f79SPaul Luse 		 * represents the blobid that was created in position n
1164e5044f79SPaul Luse 		 * so fish this out now and use it here.
1165e5044f79SPaul Luse 		 */
1166e5044f79SPaul Luse 		cli_context->argv[cli_context->argc] = strdup(tok);
1167e5044f79SPaul Luse 		if (tok[0] == '$' && tok[1] == 'B') {
1168e5044f79SPaul Luse 			tok += 2;
1169e5044f79SPaul Luse 			blob_num = atoi(tok);
11705589fc4aSPaul Luse 			if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) {
1171e5044f79SPaul Luse 				cli_context->argv[cli_context->argc] =
1172e5044f79SPaul Luse 					realloc(cli_context->argv[cli_context->argc], BUFSIZE);
1173e5044f79SPaul Luse 				if (cli_context->argv[cli_context->argc] == NULL) {
1174e5044f79SPaul Luse 					printf("ERROR: unable to realloc memory\n");
1175e5044f79SPaul Luse 					spdk_app_stop(-1);
1176e5044f79SPaul Luse 				}
1177e5044f79SPaul Luse 				if (g_script.blobid[blob_num] == 0) {
1178e5044f79SPaul Luse 					printf("ERROR: There is no blob for $B%d\n",
1179e5044f79SPaul Luse 					       blob_num);
1180e5044f79SPaul Luse 				}
1181e5044f79SPaul Luse 				snprintf(cli_context->argv[cli_context->argc], BUFSIZE,
1182e5044f79SPaul Luse 					 "%" PRIu64, g_script.blobid[blob_num]);
11835589fc4aSPaul Luse 			} else {
11845589fc4aSPaul Luse 				printf("ERROR: Invalid token or exceeded max blobs of %d\n",
11855589fc4aSPaul Luse 				       MAX_SCRIPT_BLOBS);
11865589fc4aSPaul Luse 			}
1187e5044f79SPaul Luse 		}
1188e5044f79SPaul Luse 		cli_context->argc++;
1189e5044f79SPaul Luse 		tok = strtok(NULL, " ");
1190e5044f79SPaul Luse 	}
1191e5044f79SPaul Luse 
1192e5044f79SPaul Luse 	/* call parse cmd line with user input as args */
1193e5044f79SPaul Luse 	cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context);
1194e5044f79SPaul Luse 
1195e5044f79SPaul Luse 	/* free strdup memory and reset arg count for next shell interaction */
1196e5044f79SPaul Luse 	for (i = start_idx; i < cli_context->argc; i++) {
1197e5044f79SPaul Luse 		free(cli_context->argv[i]);
1198e5044f79SPaul Luse 		cli_context->argv[i] = NULL;
1199e5044f79SPaul Luse 	}
1200e5044f79SPaul Luse 	cli_context->argc = 1;
1201e5044f79SPaul Luse 
1202e5044f79SPaul Luse 	g_script.cmdline_idx++;
1203e5044f79SPaul Luse 	assert(g_script.cmdline_idx < MAX_SCRIPT_LINES);
1204e5044f79SPaul Luse 
1205e5044f79SPaul Luse 	if (cmd_chosen == false) {
1206e5044f79SPaul Luse 		printf("ERROR: Invalid script line starting with: %s\n\n",
1207e5044f79SPaul Luse 		       g_script.cmdline[g_script.cmdline_idx - 1]);
1208e5044f79SPaul Luse 		if (g_script.ignore_errors == false) {
1209e5044f79SPaul Luse 			printf("** Aborting **\n");
1210e5044f79SPaul Luse 			cli_context->action = CLI_SHELL_EXIT;
1211e5044f79SPaul Luse 			cmd_chosen = true;
1212e5044f79SPaul Luse 			unload_bs(cli_context, "", 0);
1213e5044f79SPaul Luse 		} else {
1214e5044f79SPaul Luse 			printf("** Skipping **\n");
1215e5044f79SPaul Luse 		}
1216e5044f79SPaul Luse 	}
1217e5044f79SPaul Luse 
1218e5044f79SPaul Luse 	return cmd_chosen;
1219e5044f79SPaul Luse }
1220e5044f79SPaul Luse 
1221e5044f79SPaul Luse /*
1222e5044f79SPaul Luse  * For script mode, we read a series of commands from a text file and store them
1223e5044f79SPaul Luse  * in a global struct. That, along with the cli_mode that tells us we're in
1224e5044f79SPaul Luse  * script mode is what feeds the rest of the app in the same way as is it were
1225e5044f79SPaul Luse  * getting commands from shell mode.
1226e5044f79SPaul Luse  */
1227e5044f79SPaul Luse static void
1228e5044f79SPaul Luse parse_script(struct cli_context_t *cli_context)
1229e5044f79SPaul Luse {
1230e5044f79SPaul Luse 	FILE *fp = NULL;
1231e5044f79SPaul Luse 	size_t bufsize = BUFSIZE;
1232e5044f79SPaul Luse 	int64_t bytes_in = 0;
1233e5044f79SPaul Luse 	int i = 0;
1234e5044f79SPaul Luse 
1235e5044f79SPaul Luse 	/* initialize global script values */
1236e5044f79SPaul Luse 	for (i = 0; i < MAX_SCRIPT_BLOBS; i++) {
1237e5044f79SPaul Luse 		g_script.blobid[i] = 0;
1238e5044f79SPaul Luse 	}
1239e5044f79SPaul Luse 	g_script.blobid_idx = 0;
1240e5044f79SPaul Luse 	g_script.cmdline_idx = 0;
1241e5044f79SPaul Luse 	i = 0;
1242e5044f79SPaul Luse 
1243e5044f79SPaul Luse 	fp = fopen(cli_context->script_file, "r");
1244e5044f79SPaul Luse 	if (fp == NULL) {
1245e5044f79SPaul Luse 		printf("ERROR: unable to open script: %s\n",
1246e5044f79SPaul Luse 		       cli_context->script_file);
1247e5044f79SPaul Luse 		cli_cleanup(cli_context);
1248e5044f79SPaul Luse 		exit(-1);
1249e5044f79SPaul Luse 	}
1250e5044f79SPaul Luse 
1251e5044f79SPaul Luse 	do {
1252e5044f79SPaul Luse 		bytes_in = getline(&g_script.cmdline[i], &bufsize, fp);
1253e5044f79SPaul Luse 		if (bytes_in > 0) {
1254e5044f79SPaul Luse 			/* replace newline with null */
1255e5044f79SPaul Luse 			spdk_str_chomp(g_script.cmdline[i]);
1256e5044f79SPaul Luse 
1257e5044f79SPaul Luse 			/* ignore comments */
1258e5044f79SPaul Luse 			if (g_script.cmdline[i][0] != '#') {
1259e5044f79SPaul Luse 				i++;
1260e5044f79SPaul Luse 			}
1261e5044f79SPaul Luse 		}
12625589fc4aSPaul Luse 	} while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1);
1263e5044f79SPaul Luse 	fclose(fp);
1264e5044f79SPaul Luse 
1265e5044f79SPaul Luse 	/* add an exit cmd in case they didn't */
1266e5044f79SPaul Luse 	g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE);
1267e5044f79SPaul Luse 	if (g_script.cmdline[i] == NULL)  {
1268e5044f79SPaul Luse 		int j;
1269e5044f79SPaul Luse 
1270e5044f79SPaul Luse 		for (j = 0; j < i; j++) {
1271e5044f79SPaul Luse 			free(g_script.cmdline[j]);
1272e5044f79SPaul Luse 			g_script.cmdline[j] = NULL;
1273e5044f79SPaul Luse 		}
1274e5044f79SPaul Luse 		unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0);
1275e5044f79SPaul Luse 	}
1276e5044f79SPaul Luse 	snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X");
1277e5044f79SPaul Luse 	g_script.max_index = i;
1278e5044f79SPaul Luse }
1279e5044f79SPaul Luse 
1280e5044f79SPaul Luse /*
1281c5e63c71SPaul Luse  * Provides for a shell interface as opposed to one shot command line.
1282c5e63c71SPaul Luse  */
1283c5e63c71SPaul Luse static bool
1284c5e63c71SPaul Luse cli_shell(void *arg1, void *arg2)
1285c5e63c71SPaul Luse {
1286c5e63c71SPaul Luse 	struct cli_context_t *cli_context = arg1;
1287c5e63c71SPaul Luse 	char *line = NULL;
1288c5e63c71SPaul Luse 	ssize_t buf_size = 0;
1289c5e63c71SPaul Luse 	ssize_t bytes_in = 0;
1290c5e63c71SPaul Luse 	ssize_t tok_len = 0;
1291c5e63c71SPaul Luse 	char *tok = NULL;
1292c5e63c71SPaul Luse 	bool cmd_chosen = false;
1293c5e63c71SPaul Luse 	int start_idx = cli_context->argc;
1294c5e63c71SPaul Luse 	int i;
1295c5e63c71SPaul Luse 
1296c5e63c71SPaul Luse 	printf("blob> ");
1297c5e63c71SPaul Luse 	bytes_in = getline(&line, &buf_size, stdin);
1298c5e63c71SPaul Luse 
1299168bc9f1SDaniel Verkamp 	/* If getline() failed (EOF), exit the shell. */
1300168bc9f1SDaniel Verkamp 	if (bytes_in < 0) {
1301168bc9f1SDaniel Verkamp 		free(line);
1302168bc9f1SDaniel Verkamp 		cli_context->action = CLI_SHELL_EXIT;
1303168bc9f1SDaniel Verkamp 		return true;
1304168bc9f1SDaniel Verkamp 	}
1305168bc9f1SDaniel Verkamp 
1306c5e63c71SPaul Luse 	/* parse input and update cli_context so we can use common option parser */
1307c5e63c71SPaul Luse 	if (bytes_in > 0) {
1308c5e63c71SPaul Luse 		tok = strtok(line, " ");
1309c5e63c71SPaul Luse 	}
1310c5e63c71SPaul Luse 	while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) {
1311c5e63c71SPaul Luse 		cli_context->argv[cli_context->argc] = strdup(tok);
1312c5e63c71SPaul Luse 		tok_len = strlen(tok);
1313c5e63c71SPaul Luse 		cli_context->argc++;
131498a2ec3eSPaul Luse 		tok = strtok(NULL, " ");
1315c5e63c71SPaul Luse 	}
1316c5e63c71SPaul Luse 
1317c5e63c71SPaul Luse 	/* replace newline on last arg with null */
1318c5e63c71SPaul Luse 	if (tok_len) {
1319c5e63c71SPaul Luse 		spdk_str_chomp(cli_context->argv[cli_context->argc - 1]);
1320c5e63c71SPaul Luse 	}
1321c5e63c71SPaul Luse 
1322c5e63c71SPaul Luse 	/* call parse cmd line with user input as args */
1323c5e63c71SPaul Luse 	cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context);
1324c5e63c71SPaul Luse 
1325c5e63c71SPaul Luse 	/* free strdup mem & reset arg count for next shell interaction */
1326c5e63c71SPaul Luse 	for (i = start_idx; i < cli_context->argc; i++) {
1327c5e63c71SPaul Luse 		free(cli_context->argv[i]);
1328d4e565b4SPaul Luse 		cli_context->argv[i] = NULL;
1329c5e63c71SPaul Luse 	}
1330c5e63c71SPaul Luse 	cli_context->argc = 1;
1331c5e63c71SPaul Luse 
1332c5e63c71SPaul Luse 	free(line);
1333c5e63c71SPaul Luse 
1334c5e63c71SPaul Luse 	return cmd_chosen;
1335c5e63c71SPaul Luse }
1336c5e63c71SPaul Luse 
1337866f15b3SPaul Luse /*
1338866f15b3SPaul Luse  * This is the function we pass into the SPDK framework that gets
1339866f15b3SPaul Luse  * called first.
1340866f15b3SPaul Luse  */
1341866f15b3SPaul Luse static void
1342866f15b3SPaul Luse cli_start(void *arg1, void *arg2)
1343866f15b3SPaul Luse {
1344866f15b3SPaul Luse 	struct cli_context_t *cli_context = arg1;
1345866f15b3SPaul Luse 
1346866f15b3SPaul Luse 	/*
1347e5044f79SPaul Luse 	 * If we're in script mode, we already have a list of commands so
1348e5044f79SPaul Luse 	 * just need to pull them out one at a time and process them.
1349e5044f79SPaul Luse 	 */
1350e5044f79SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
1351e5044f79SPaul Luse 		while (line_parser(cli_context) == false);
1352e5044f79SPaul Luse 	}
1353e5044f79SPaul Luse 
1354e5044f79SPaul Luse 	/*
1355866f15b3SPaul Luse 	 * The initial cmd line options are parsed once before this function is
1356866f15b3SPaul Luse 	 * called so if there is no action, we're in shell mode and will loop
1357866f15b3SPaul Luse 	 * here until a a valid option is parsed and returned.
1358866f15b3SPaul Luse 	 */
1359866f15b3SPaul Luse 	if (cli_context->action == CLI_NONE) {
1360866f15b3SPaul Luse 		while (cli_shell(cli_context, NULL) == false);
1361866f15b3SPaul Luse 	}
1362866f15b3SPaul Luse 
1363866f15b3SPaul Luse 	/* Decide what to do next based on cmd line parsing. */
1364866f15b3SPaul Luse 	switch (cli_context->action) {
1365866f15b3SPaul Luse 	case CLI_SET_SUPER:
1366866f15b3SPaul Luse 	case CLI_SHOW_BS:
1367866f15b3SPaul Luse 	case CLI_CREATE_BLOB:
1368866f15b3SPaul Luse 	case CLI_SET_XATTR:
1369866f15b3SPaul Luse 	case CLI_REM_XATTR:
1370866f15b3SPaul Luse 	case CLI_SHOW_BLOB:
1371866f15b3SPaul Luse 	case CLI_LIST_BLOBS:
13724952f254SDaniel Verkamp 	case CLI_DUMP_BLOB:
13734952f254SDaniel Verkamp 	case CLI_IMPORT_BLOB:
1374866f15b3SPaul Luse 	case CLI_FILL:
1375866f15b3SPaul Luse 		load_bs(cli_context);
1376866f15b3SPaul Luse 		break;
1377866f15b3SPaul Luse 	case CLI_INIT_BS:
1378866f15b3SPaul Luse 		init_bs(cli_context);
1379866f15b3SPaul Luse 		break;
1380866f15b3SPaul Luse 	case CLI_LIST_BDEVS:
1381866f15b3SPaul Luse 		list_bdevs(cli_context);
1382866f15b3SPaul Luse 		break;
1383866f15b3SPaul Luse 	case CLI_SHELL_EXIT:
1384866f15b3SPaul Luse 		/*
1385866f15b3SPaul Luse 		 * Because shell mode reuses cmd mode functions, the blobstore
1386866f15b3SPaul Luse 		 * is loaded/unloaded with every action so we just need to
1387866f15b3SPaul Luse 		 * stop the framework. For this app there's no need to optimize
1388866f15b3SPaul Luse 		 * and keep the blobstore open while the app is in shell mode.
1389866f15b3SPaul Luse 		 */
1390866f15b3SPaul Luse 		spdk_app_stop(0);
1391866f15b3SPaul Luse 		break;
1392866f15b3SPaul Luse 	case CLI_HELP:
1393e5044f79SPaul Luse 		usage(cli_context, "");
1394866f15b3SPaul Luse 		unload_complete(cli_context, 0);
1395866f15b3SPaul Luse 		break;
1396866f15b3SPaul Luse 	default:
1397866f15b3SPaul Luse 		/* should never get here */
139898a2ec3eSPaul Luse 		exit(-1);
1399866f15b3SPaul Luse 		break;
1400866f15b3SPaul Luse 	}
1401866f15b3SPaul Luse }
1402c5e63c71SPaul Luse 
1403c5e63c71SPaul Luse int
1404c5e63c71SPaul Luse main(int argc, char **argv)
1405c5e63c71SPaul Luse {
1406c5e63c71SPaul Luse 	struct spdk_app_opts opts = {};
1407c5e63c71SPaul Luse 	struct cli_context_t *cli_context = NULL;
1408e5044f79SPaul Luse 	bool cmd_chosen;
1409c5e63c71SPaul Luse 	int rc = 0;
1410c5e63c71SPaul Luse 
1411c5e63c71SPaul Luse 	if (argc < 2) {
1412e5044f79SPaul Luse 		usage(cli_context, "ERROR: Invalid option\n");
1413e5044f79SPaul Luse 		exit(-1);
1414c5e63c71SPaul Luse 	}
1415c5e63c71SPaul Luse 
1416c5e63c71SPaul Luse 	cli_context = calloc(1, sizeof(struct cli_context_t));
1417c5e63c71SPaul Luse 	if (cli_context == NULL) {
1418c5e63c71SPaul Luse 		printf("ERROR: could not allocate context structure\n");
1419c5e63c71SPaul Luse 		exit(-1);
1420c5e63c71SPaul Luse 	}
1421cfa4cae0SPaul Luse 
1422c5e63c71SPaul Luse 	/* default to CMD mode until we've parsed the first parms */
1423c5e63c71SPaul Luse 	cli_context->cli_mode = CLI_MODE_CMD;
1424c5e63c71SPaul Luse 	cli_context->argv[0] = strdup(argv[0]);
1425c5e63c71SPaul Luse 	cli_context->argc = 1;
1426c5e63c71SPaul Luse 
1427c5e63c71SPaul Luse 	/* parse command line */
1428e5044f79SPaul Luse 	cmd_chosen = cmd_parser(argc, argv, cli_context);
1429c5e63c71SPaul Luse 	free(cli_context->argv[0]);
1430e5044f79SPaul Luse 	cli_context->argv[0] = NULL;
1431e5044f79SPaul Luse 	if (cmd_chosen == false) {
1432e5044f79SPaul Luse 		cli_cleanup(cli_context);
1433e5044f79SPaul Luse 		exit(-1);
1434e5044f79SPaul Luse 	}
1435c5e63c71SPaul Luse 
1436c5e63c71SPaul Luse 	/* after displaying help, just exit */
1437c5e63c71SPaul Luse 	if (cli_context->action == CLI_HELP) {
1438e5044f79SPaul Luse 		usage(cli_context, "");
1439c5e63c71SPaul Luse 		cli_cleanup(cli_context);
1440c5e63c71SPaul Luse 		exit(-1);
1441c5e63c71SPaul Luse 	}
1442c5e63c71SPaul Luse 
1443c5e63c71SPaul Luse 	/* if they don't supply a conf name, use the default */
1444c5e63c71SPaul Luse 	if (!cli_context->config_file) {
1445c5e63c71SPaul Luse 		cli_context->config_file = program_conf;
1446c5e63c71SPaul Luse 	}
1447c5e63c71SPaul Luse 
1448c5e63c71SPaul Luse 	/* if the config file doesn't exist, tell them how to make one */
1449c5e63c71SPaul Luse 	if (access(cli_context->config_file, F_OK) == -1) {
1450c5e63c71SPaul Luse 		printf("Error: No config file found.\n");
1451c5e63c71SPaul Luse 		printf("To create a config file named 'blobcli.conf' for your NVMe device:\n");
1452c5e63c71SPaul Luse 		printf("   <path to spdk>/scripts/gen_nvme.sh > blobcli.conf\n");
1453c5e63c71SPaul Luse 		printf("and then re-run the cli tool.\n");
145498a2ec3eSPaul Luse 		exit(-1);
1455c5e63c71SPaul Luse 	}
1456c5e63c71SPaul Luse 
1457e5044f79SPaul Luse 	/*
1458e5044f79SPaul Luse 	 * For script mode we keep a bunch of stuff in a global since
1459e5044f79SPaul Luse 	 * none if it is passed back and forth to SPDK.
1460e5044f79SPaul Luse 	 */
1461e5044f79SPaul Luse 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
1462e5044f79SPaul Luse 		/*
1463e5044f79SPaul Luse 		 * Now we'll build up the global which will direct this run of the app
1464e5044f79SPaul Luse 		 * as it will have a list (g_script) of all of the commands line by
1465e5044f79SPaul Luse 		 * line as if they were typed in on the shell at cmd line.
1466e5044f79SPaul Luse 		 */
1467e5044f79SPaul Luse 		parse_script(cli_context);
1468e5044f79SPaul Luse 	}
1469e5044f79SPaul Luse 
14708fbbfccdSPaul Luse 	/* Set default values in opts struct along with name and conf file. */
14718fbbfccdSPaul Luse 	spdk_app_opts_init(&opts);
14728fbbfccdSPaul Luse 	opts.name = "blobcli";
1473c5e63c71SPaul Luse 	opts.config_file = cli_context->config_file;
14748fbbfccdSPaul Luse 
1475c5e63c71SPaul Luse 	cli_context->app_started = true;
14768fbbfccdSPaul Luse 	rc = spdk_app_start(&opts, cli_start, cli_context, NULL);
14778fbbfccdSPaul Luse 	if (rc) {
14788fbbfccdSPaul Luse 		printf("ERROR!\n");
14798fbbfccdSPaul Luse 	}
14808fbbfccdSPaul Luse 
14818fbbfccdSPaul Luse 	/* Free up memory that we allocated */
14828fbbfccdSPaul Luse 	cli_cleanup(cli_context);
14838fbbfccdSPaul Luse 
14848fbbfccdSPaul Luse 	/* Gracefully close out all of the SPDK subsystems. */
14858fbbfccdSPaul Luse 	spdk_app_fini();
14868fbbfccdSPaul Luse 	return rc;
14878fbbfccdSPaul Luse }
1488