xref: /spdk/examples/blob/cli/blobcli.c (revision db26b96fc0a2862b2f72fd2ffcf1c274d25a9d68)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk/bdev.h"
37 #include "spdk/env.h"
38 #include "spdk/event.h"
39 #include "spdk/blob_bdev.h"
40 #include "spdk/blob.h"
41 #include "spdk/log.h"
42 #include "spdk/version.h"
43 #include "spdk/string.h"
44 
45 /*
46  * The following is not a public header file, but the CLI does expose
47  * some internals of blobstore for dev/debug puposes so we
48  * include it here.
49  */
50 #include "../lib/blob/blobstore.h"
51 static void cli_start(void *arg1, void *arg2);
52 
53 static const char *program_name = "blobcli";
54 /* default name for .conf file, any name can be used however with -c switch */
55 static const char *program_conf = "blobcli.conf";
56 
57 /*
58  * CMD mode runs one command at a time which can be annoying as the init takes
59  * a few seconds, so the shell mode, invoked with -S, does the init once and gives
60  * the user an interactive shell instead. With script mode init is also done just
61  * once.
62  */
63 enum cli_mode_type {
64 	CLI_MODE_CMD,
65 	CLI_MODE_SHELL,
66 	CLI_MODE_SCRIPT
67 };
68 
69 enum cli_action_type {
70 	CLI_NONE,
71 	CLI_IMPORT,
72 	CLI_DUMP,
73 	CLI_FILL,
74 	CLI_REM_XATTR,
75 	CLI_SET_XATTR,
76 	CLI_SET_SUPER,
77 	CLI_SHOW_BS,
78 	CLI_SHOW_BLOB,
79 	CLI_CREATE_BLOB,
80 	CLI_LIST_BDEVS,
81 	CLI_LIST_BLOBS,
82 	CLI_INIT_BS,
83 	CLI_SHELL_EXIT,
84 	CLI_HELP,
85 };
86 
87 #define BUFSIZE 255
88 #define MAX_ARGS 16
89 #define ALIGN_4K 4096
90 #define STARTING_PAGE 0
91 #define NUM_PAGES 1
92 
93 /*
94  * The CLI uses the SPDK app framework so is async and callback driven. A
95  * pointer to this structure is passed to SPDK calls and returned in the
96  * callbacks for easy access to all the info we may need.
97  */
98 struct cli_context_t {
99 	struct spdk_blob_store *bs;
100 	struct spdk_blob *blob;
101 	struct spdk_bs_dev *bs_dev;
102 	spdk_blob_id blobid;
103 	spdk_blob_id superid;
104 	struct spdk_io_channel *channel;
105 	uint8_t *buff;
106 	uint64_t page_size;
107 	uint64_t page_count;
108 	uint64_t blob_pages;
109 	uint64_t bytes_so_far;
110 	FILE *fp;
111 	enum cli_action_type action;
112 	char key[BUFSIZE + 1];
113 	char value[BUFSIZE + 1];
114 	char file[BUFSIZE + 1];
115 	uint64_t filesize;
116 	int fill_value;
117 	char bdev_name[BUFSIZE];
118 	int rc;
119 	int num_clusters;
120 	enum cli_mode_type cli_mode;
121 	const char *config_file;
122 	int argc;
123 	char *argv[MAX_ARGS];
124 	bool app_started;
125 	char script_file[BUFSIZE + 1];
126 };
127 
128 /* we store a bunch of stuff in a global struct for use by scripting mode */
129 #define MAX_SCRIPT_LINES 64
130 #define MAX_SCRIPT_BLOBS 16
131 struct cli_script_t {
132 	spdk_blob_id blobid[MAX_SCRIPT_BLOBS];
133 	int blobid_idx;
134 	int max_index;
135 	int cmdline_idx;
136 	bool ignore_errors;
137 	char *cmdline[MAX_SCRIPT_LINES];
138 };
139 struct cli_script_t g_script;
140 
141 /*
142  * Common printing of commands for CLI and shell modes.
143  */
144 static void
145 print_cmds(void)
146 {
147 	printf("\nCommands include:\n");
148 	printf("\t-b bdev - name of the block device to use (example: Nvme0n1)\n");
149 	printf("\t-d <blobid> filename - dump contents of a blob to a file\n");
150 	printf("\t-f <blobid> value - fill a blob with a decimal value\n");
151 	printf("\t-h - this help screen\n");
152 	printf("\t-i - initialize a blobstore\n");
153 	printf("\t-l bdevs | blobs - list either available bdevs or existing blobs\n");
154 	printf("\t-m <blobid> filename - import contents of a file to a blob\n");
155 	printf("\t-n <# clusters> - create new blob\n");
156 	printf("\t-p <blobid> - set the superblob to the ID provided\n");
157 	printf("\t-r <blobid> name - remove xattr name/value pair\n");
158 	printf("\t-s <blobid> | bs - show blob info or blobstore info\n");
159 	printf("\t-x <blobid> name value - set xattr name/value pair\n");
160 	printf("\t-X - exit when in interactive shell mode\n");
161 	printf("\t-S - enter interactive shell mode\n");
162 	printf("\t-T <filename> - automated script mode\n");
163 	printf("\n");
164 }
165 
166 /*
167  * Prints usage and relevant error message.
168  */
169 static void
170 usage(struct cli_context_t *cli_context, char *msg)
171 {
172 	if (msg) {
173 		printf("%s", msg);
174 	}
175 
176 	if (cli_context && cli_context->cli_mode == CLI_MODE_CMD) {
177 		printf("Version %s\n", SPDK_VERSION_STRING);
178 		printf("Usage: %s [-c SPDK config_file] Command\n", program_name);
179 		printf("\n%s is a command line tool for interacting with blobstore\n",
180 		       program_name);
181 		printf("on the underlying device specified in the conf file passed\n");
182 		printf("in as a command line option.\n");
183 	}
184 	if (cli_context && cli_context->cli_mode != CLI_MODE_SCRIPT) {
185 		print_cmds();
186 	}
187 }
188 
189 /*
190  * Free up memory that we allocated.
191  */
192 static void
193 cli_cleanup(struct cli_context_t *cli_context)
194 {
195 	if (cli_context->buff) {
196 		spdk_dma_free(cli_context->buff);
197 	}
198 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
199 		int i;
200 
201 		for (i = 0; i <= g_script.max_index; i++) {
202 			free(g_script.cmdline[i]);
203 		}
204 	}
205 	free(cli_context);
206 }
207 
208 /*
209  * Callback routine for the blobstore unload.
210  */
211 static void
212 unload_complete(void *cb_arg, int bserrno)
213 {
214 	struct cli_context_t *cli_context = cb_arg;
215 
216 	if (bserrno) {
217 		printf("Error %d unloading the bobstore\n", bserrno);
218 		cli_context->rc = bserrno;
219 	}
220 
221 	/*
222 	 * Quit if we're in cmd mode or exiting shell mode, otherwise
223 	 * clear the action field and start the main function again.
224 	 */
225 	if (cli_context->cli_mode == CLI_MODE_CMD ||
226 	    cli_context->action == CLI_SHELL_EXIT) {
227 		spdk_app_stop(cli_context->rc);
228 	} else {
229 		/* when action is CLI_NONE, we know we need to remain in the shell */
230 		cli_context->bs = NULL;
231 		cli_context->action = CLI_NONE;
232 		cli_start(cli_context, NULL);
233 	}
234 }
235 
236 /*
237  * Unload the blobstore.
238  */
239 static void
240 unload_bs(struct cli_context_t *cli_context, char *msg, int bserrno)
241 {
242 	if (bserrno) {
243 		printf("%s (err %d)\n", msg, bserrno);
244 		cli_context->rc = bserrno;
245 	}
246 
247 	if (cli_context->bs) {
248 		if (cli_context->channel) {
249 			spdk_bs_free_io_channel(cli_context->channel);
250 			cli_context->channel = NULL;
251 		}
252 		spdk_bs_unload(cli_context->bs, unload_complete, cli_context);
253 	} else {
254 		spdk_app_stop(bserrno);
255 	}
256 }
257 
258 /*
259  * Callback for closing a blob.
260  */
261 static void
262 close_cb(void *arg1, int bserrno)
263 {
264 	struct cli_context_t *cli_context = arg1;
265 
266 	if (bserrno) {
267 		unload_bs(cli_context, "Error in close callback",
268 			  bserrno);
269 		return;
270 	}
271 	unload_bs(cli_context, "", 0);
272 }
273 
274 /*
275  * Callback function for sync'ing metadata.
276  */
277 static void
278 sync_cb(void *arg1, int bserrno)
279 {
280 	struct cli_context_t *cli_context = arg1;
281 
282 	if (bserrno) {
283 		unload_bs(cli_context, "Error in sync callback",
284 			  bserrno);
285 		return;
286 	}
287 
288 	spdk_bs_md_close_blob(&cli_context->blob, close_cb,
289 			      cli_context);
290 }
291 
292 /*
293  * Callback function for opening a blob after creating.
294  */
295 static void
296 open_now_resize_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
297 {
298 	struct cli_context_t *cli_context = cb_arg;
299 	int rc = 0;
300 	uint64_t total = 0;
301 
302 	if (bserrno) {
303 		unload_bs(cli_context, "Error in open completion",
304 			  bserrno);
305 		return;
306 	}
307 	cli_context->blob = blob;
308 
309 	rc = spdk_bs_md_resize_blob(cli_context->blob,
310 				    cli_context->num_clusters);
311 	if (rc) {
312 		unload_bs(cli_context, "Error in blob resize",
313 			  bserrno);
314 		return;
315 	}
316 
317 	total = spdk_blob_get_num_clusters(cli_context->blob);
318 	printf("blob now has USED clusters of %" PRIu64 "\n",
319 	       total);
320 
321 	/*
322 	 * Always a good idea to sync after MD changes or the changes
323 	 * may be lost if things aren't closed cleanly.
324 	 */
325 	spdk_bs_md_sync_blob(cli_context->blob, sync_cb,
326 			     cli_context);
327 }
328 
329 /*
330  * Callback function for creating a blob.
331  */
332 static void
333 blob_create_cb(void *arg1, spdk_blob_id blobid, int bserrno)
334 {
335 	struct cli_context_t *cli_context = arg1;
336 
337 	if (bserrno) {
338 		unload_bs(cli_context, "Error in blob create callback",
339 			  bserrno);
340 		return;
341 	}
342 
343 	cli_context->blobid = blobid;
344 	printf("New blob id %" PRIu64 "\n", cli_context->blobid);
345 
346 	/* if we're in script mode, we need info on all blobids for later */
347 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
348 		g_script.blobid[g_script.blobid_idx++] = blobid;
349 	}
350 
351 	/* We have to open the blob before we can do things like resize. */
352 	spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid,
353 			     open_now_resize_cb, cli_context);
354 }
355 
356 /*
357  * Callback for get_super where we'll continue on to show blobstore info.
358  */
359 static void
360 show_bs_cb(void *arg1, spdk_blob_id blobid, int bserrno)
361 {
362 	struct cli_context_t *cli_context = arg1;
363 	struct spdk_bs_type bstype;
364 	uint64_t val;
365 	struct spdk_bdev *bdev = NULL;
366 
367 	if (bserrno && bserrno != -ENOENT) {
368 		unload_bs(cli_context, "Error in get_super callback",
369 			  bserrno);
370 		return;
371 	}
372 	cli_context->superid = blobid;
373 
374 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
375 	if (bdev == NULL) {
376 		unload_bs(cli_context, "Error w/bdev in get_super callback",
377 			  bserrno);
378 		return;
379 	}
380 
381 	printf("Blobstore Public Info:\n");
382 	printf("\tUsing bdev Product Name: %s\n",
383 	       spdk_bdev_get_product_name(bdev));
384 	printf("\tAPI Version: %d\n", SPDK_BS_VERSION);
385 
386 	if (bserrno != -ENOENT) {
387 		printf("\tsuper blob ID: %" PRIu64 "\n", cli_context->superid);
388 	} else {
389 		printf("\tsuper blob ID: none assigned\n");
390 	}
391 
392 	printf("\tpage size: %" PRIu64 "\n", cli_context->page_size);
393 
394 	val = spdk_bs_get_cluster_size(cli_context->bs);
395 	printf("\tcluster size: %" PRIu64 "\n", val);
396 
397 	val = spdk_bs_free_cluster_count(cli_context->bs);
398 	printf("\t# free clusters: %" PRIu64 "\n", val);
399 
400 	bstype = spdk_bs_get_bstype(cli_context->bs);
401 	spdk_trace_dump(stdout, "\tblobstore type:", &bstype, sizeof(bstype));
402 
403 	/*
404 	 * Private info isn't accessible via the public API but
405 	 * may be useful for debug of blobstore based applications.
406 	 */
407 	printf("\nBlobstore Private Info:\n");
408 	printf("\tMetadata start (pages): %" PRIu64 "\n",
409 	       cli_context->bs->md_start);
410 	printf("\tMetadata length (pages): %d \n",
411 	       cli_context->bs->md_len);
412 
413 	unload_bs(cli_context, "", 0);
414 }
415 
416 /*
417  * Show detailed info about a particular blob.
418  */
419 static void
420 show_blob(struct cli_context_t *cli_context)
421 {
422 	uint64_t val;
423 	struct spdk_xattr_names *names;
424 	const void *value;
425 	size_t value_len;
426 	char data[BUFSIZE];
427 	unsigned int i;
428 
429 	printf("Blob Public Info:\n");
430 
431 	printf("blob ID: %" PRIu64 "\n", cli_context->blobid);
432 
433 	val = spdk_blob_get_num_clusters(cli_context->blob);
434 	printf("# of clusters: %" PRIu64 "\n", val);
435 
436 	printf("# of bytes: %" PRIu64 "\n",
437 	       val * spdk_bs_get_cluster_size(cli_context->bs));
438 
439 	val = spdk_blob_get_num_pages(cli_context->blob);
440 	printf("# of pages: %" PRIu64 "\n", val);
441 
442 	spdk_bs_md_get_xattr_names(cli_context->blob, &names);
443 
444 	printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names));
445 	printf("xattrs:\n");
446 	for (i = 0; i < spdk_xattr_names_get_count(names); i++) {
447 		spdk_bs_md_get_xattr_value(cli_context->blob,
448 					   spdk_xattr_names_get_name(names, i),
449 					   &value, &value_len);
450 		if ((value_len + 1) > sizeof(data)) {
451 			printf("FYI: adjusting size of xattr due to CLI limits.\n");
452 			value_len = sizeof(data) - 1;
453 		}
454 		memcpy(&data, value, value_len);
455 		data[value_len] = '\0';
456 		printf("\n(%d) Name:%s\n", i,
457 		       spdk_xattr_names_get_name(names, i));
458 		printf("(%d) Value:\n", i);
459 		spdk_trace_dump(stdout, "", value, value_len);
460 	}
461 
462 	/*
463 	 * Private info isn't accessible via the public API but
464 	 * may be useful for debug of blobstore based applications.
465 	 */
466 	printf("\nBlob Private Info:\n");
467 	switch (cli_context->blob->state) {
468 	case SPDK_BLOB_STATE_DIRTY:
469 		printf("state: DIRTY\n");
470 		break;
471 	case SPDK_BLOB_STATE_CLEAN:
472 		printf("state: CLEAN\n");
473 		break;
474 	case SPDK_BLOB_STATE_LOADING:
475 		printf("state: LOADING\n");
476 		break;
477 	case SPDK_BLOB_STATE_SYNCING:
478 		printf("state: SYNCING\n");
479 		break;
480 	default:
481 		printf("state: UNKNOWN\n");
482 		break;
483 	}
484 	printf("open ref count: %d\n",
485 	       cli_context->blob->open_ref);
486 
487 	spdk_xattr_names_free(names);
488 }
489 
490 /*
491  * Callback for getting the first blob, shared with simple blob listing as well.
492  */
493 static void
494 blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno)
495 {
496 	struct cli_context_t *cli_context = arg1;
497 
498 	if (bserrno) {
499 		if (bserrno == -ENOENT) {
500 			/* this simply means there are no more blobs */
501 			unload_bs(cli_context, "", 0);
502 		} else {
503 			unload_bs(cli_context, "Error in blob iter callback",
504 				  bserrno);
505 		}
506 		return;
507 	}
508 
509 	if (cli_context->action == CLI_LIST_BLOBS) {
510 		printf("\nList BLOBS:\n");
511 		printf("Found blob with ID# %" PRIu64 "\n",
512 		       spdk_blob_get_id(blob));
513 	} else if (spdk_blob_get_id(blob) == cli_context->blobid) {
514 		/*
515 		 * Found the blob we're looking for, but we need to finish
516 		 * iterating even after showing the info so that internally
517 		 * the blobstore logic will close the blob. Or we could
518 		 * chose to close it now, either way.
519 		 */
520 		cli_context->blob = blob;
521 		show_blob(cli_context);
522 	}
523 
524 	spdk_bs_md_iter_next(cli_context->bs, &blob, blob_iter_cb,
525 			     cli_context);
526 }
527 
528 /*
529  * Callback for setting the super blob ID.
530  */
531 static void
532 set_super_cb(void *arg1, int bserrno)
533 {
534 	struct cli_context_t *cli_context = arg1;
535 
536 	if (bserrno) {
537 		unload_bs(cli_context, "Error in set_super callback",
538 			  bserrno);
539 		return;
540 	}
541 
542 	printf("Super Blob ID has been set.\n");
543 	unload_bs(cli_context, "", 0);
544 }
545 
546 /*
547  * Callback for set_xattr_open where we set or delete xattrs.
548  */
549 static void
550 set_xattr_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
551 {
552 	struct cli_context_t *cli_context = cb_arg;
553 
554 	if (bserrno) {
555 		unload_bs(cli_context, "Error in blob open callback",
556 			  bserrno);
557 		return;
558 	}
559 	cli_context->blob = blob;
560 
561 	if (cli_context->action == CLI_SET_XATTR) {
562 		spdk_blob_md_set_xattr(cli_context->blob,
563 				       cli_context->key,
564 				       cli_context->value,
565 				       strlen(cli_context->value) + 1);
566 		printf("Xattr has been set.\n");
567 	} else {
568 		spdk_blob_md_remove_xattr(cli_context->blob,
569 					  cli_context->key);
570 		printf("Xattr has been removed.\n");
571 	}
572 
573 	spdk_bs_md_sync_blob(cli_context->blob, sync_cb, cli_context);
574 }
575 
576 /*
577  * Callback function for reading a blob for dumping to a file.
578  */
579 static void
580 read_dump_cb(void *arg1, int bserrno)
581 {
582 	struct cli_context_t *cli_context = arg1;
583 	uint64_t bytes_written;
584 
585 	if (bserrno) {
586 		fclose(cli_context->fp);
587 		unload_bs(cli_context, "Error in read completion",
588 			  bserrno);
589 		return;
590 	}
591 
592 	bytes_written = fwrite(cli_context->buff, NUM_PAGES, cli_context->page_size,
593 			       cli_context->fp);
594 	if (bytes_written != cli_context->page_size) {
595 		fclose(cli_context->fp);
596 		unload_bs(cli_context, "Error with fwrite",
597 			  bserrno);
598 		return;
599 	}
600 
601 	printf(".");
602 	if (++cli_context->page_count < cli_context->blob_pages) {
603 		/* perform another read */
604 		spdk_bs_io_read_blob(cli_context->blob, cli_context->channel,
605 				     cli_context->buff, cli_context->page_count,
606 				     NUM_PAGES, read_dump_cb, cli_context);
607 	} else {
608 		/* done reading */
609 		printf("\nFile write complete (to %s).\n", cli_context->file);
610 		fclose(cli_context->fp);
611 		spdk_bs_md_close_blob(&cli_context->blob, close_cb,
612 				      cli_context);
613 	}
614 }
615 
616 /*
617  * Callback for write completion on the import of a file to a blob.
618  */
619 static void
620 write_imp_cb(void *arg1, int bserrno)
621 {
622 	struct cli_context_t *cli_context = arg1;
623 	uint64_t bytes_read;
624 
625 	if (bserrno) {
626 		fclose(cli_context->fp);
627 		unload_bs(cli_context, "Error in write completion",
628 			  bserrno);
629 		return;
630 	}
631 
632 	if (cli_context->bytes_so_far < cli_context->filesize) {
633 		/* perform another file read */
634 		bytes_read = fread(cli_context->buff, 1,
635 				   cli_context->page_size,
636 				   cli_context->fp);
637 		cli_context->bytes_so_far += bytes_read;
638 
639 		/* if this read is < 1 page, fill with 0s */
640 		if (bytes_read < cli_context->page_size) {
641 			uint8_t *offset = cli_context->buff + bytes_read;
642 			memset(offset, 0, cli_context->page_size - bytes_read);
643 		}
644 	} else {
645 		/*
646 		 * Done reading the file, fill the rest of the blob with 0s,
647 		 * yeah we're memsetting the same page over and over here
648 		 */
649 		memset(cli_context->buff, 0, cli_context->page_size);
650 	}
651 	if (++cli_context->page_count < cli_context->blob_pages) {
652 		printf(".");
653 		spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
654 				      cli_context->buff, cli_context->page_count,
655 				      NUM_PAGES, write_imp_cb, cli_context);
656 	} else {
657 		/* done writing */
658 		printf("\nBlob import complete (from %s).\n", cli_context->file);
659 		fclose(cli_context->fp);
660 		spdk_bs_md_close_blob(&cli_context->blob, close_cb, cli_context);
661 	}
662 }
663 
664 /*
665  * Callback for open blobs where we'll continue on dump a blob to a file or
666  * import a file to a blob. For dump, the resulting file will always be the
667  * full size of the blob.  For import, the blob will fill with the file
668  * contents first and then 0 out the rest of the blob.
669  */
670 static void
671 dump_imp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
672 {
673 	struct cli_context_t *cli_context = cb_arg;
674 
675 	if (bserrno) {
676 		unload_bs(cli_context, "Error in blob open callback",
677 			  bserrno);
678 		return;
679 	}
680 	cli_context->blob = blob;
681 
682 	/*
683 	 * We'll transfer just one page at a time to keep the buffer
684 	 * small. This could be bigger of course.
685 	 */
686 	cli_context->buff = spdk_dma_malloc(cli_context->page_size,
687 					    ALIGN_4K, NULL);
688 	if (cli_context->buff == NULL) {
689 		printf("Error in allocating memory\n");
690 		spdk_bs_md_close_blob(&cli_context->blob, close_cb, cli_context);
691 		return;
692 	}
693 	printf("Working");
694 	cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob);
695 	cli_context->page_count = 0;
696 	if (cli_context->action == CLI_DUMP) {
697 		cli_context->fp = fopen(cli_context->file, "w");
698 		if (cli_context->fp == NULL) {
699 			printf("Error in opening file\n");
700 			spdk_bs_md_close_blob(&cli_context->blob, close_cb, cli_context);
701 			return;
702 		}
703 
704 		/* read a page of data from the blob */
705 		spdk_bs_io_read_blob(cli_context->blob, cli_context->channel,
706 				     cli_context->buff, cli_context->page_count,
707 				     NUM_PAGES, read_dump_cb, cli_context);
708 	} else {
709 		cli_context->fp = fopen(cli_context->file, "r");
710 		if (cli_context->fp == NULL) {
711 			printf("Error in opening file\n");
712 			spdk_bs_md_close_blob(&cli_context->blob, close_cb, cli_context);
713 			return;
714 		}
715 
716 		/* get the filesize then rewind read a page of data from file */
717 		fseek(cli_context->fp, 0L, SEEK_END);
718 		cli_context->filesize = ftell(cli_context->fp);
719 		rewind(cli_context->fp);
720 		cli_context->bytes_so_far = fread(cli_context->buff, NUM_PAGES,
721 						  cli_context->page_size,
722 						  cli_context->fp);
723 
724 		/* if the file is < a page, fill the rest with 0s */
725 		if (cli_context->filesize < cli_context->page_size) {
726 			uint8_t *offset =
727 				cli_context->buff + cli_context->filesize;
728 
729 			memset(offset, 0,
730 			       cli_context->page_size - cli_context->filesize);
731 		}
732 
733 		spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
734 				      cli_context->buff, cli_context->page_count,
735 				      NUM_PAGES, write_imp_cb, cli_context);
736 	}
737 }
738 
739 /*
740  * Callback function for writing a specific pattern to page 0.
741  */
742 static void
743 write_cb(void *arg1, int bserrno)
744 {
745 	struct cli_context_t *cli_context = arg1;
746 
747 	if (bserrno) {
748 		unload_bs(cli_context, "Error in write completion",
749 			  bserrno);
750 		return;
751 	}
752 	printf(".");
753 	if (++cli_context->page_count < cli_context->blob_pages) {
754 		spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
755 				      cli_context->buff, cli_context->page_count,
756 				      NUM_PAGES, write_cb, cli_context);
757 	} else {
758 		/* done writing */
759 		printf("\nBlob fill complete (with 0x%x).\n", cli_context->fill_value);
760 		spdk_bs_md_close_blob(&cli_context->blob, close_cb,
761 				      cli_context);
762 	}
763 }
764 
765 /*
766  * Callback function to fill a blob with a value, callback from open.
767  */
768 static void
769 fill_blob_cb(void *arg1, struct spdk_blob *blob, int bserrno)
770 {
771 	struct cli_context_t *cli_context = arg1;
772 
773 	if (bserrno) {
774 		unload_bs(cli_context, "Error in open callback",
775 			  bserrno);
776 		return;
777 	}
778 
779 	cli_context->blob = blob;
780 	cli_context->page_count = 0;
781 	cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob);
782 	cli_context->buff = spdk_dma_malloc(cli_context->page_size,
783 					    ALIGN_4K, NULL);
784 	if (cli_context->buff == NULL) {
785 		unload_bs(cli_context, "Error in allocating memory",
786 			  -ENOMEM);
787 		return;
788 	}
789 
790 	memset(cli_context->buff, cli_context->fill_value,
791 	       cli_context->page_size);
792 	printf("Working");
793 	spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
794 			      cli_context->buff,
795 			      STARTING_PAGE, NUM_PAGES, write_cb, cli_context);
796 }
797 
798 /*
799  * Multiple actions require us to open the bs first so here we use
800  * a common callback to set a bunch of values and then move on to
801  * the next step saved off via function pointer.
802  */
803 static void
804 load_bs_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
805 {
806 	struct cli_context_t *cli_context = arg1;
807 
808 	if (bserrno) {
809 		unload_bs(cli_context, "Error in load callback",
810 			  bserrno);
811 		return;
812 	}
813 
814 	cli_context->bs = bs;
815 	cli_context->page_size = spdk_bs_get_page_size(cli_context->bs);
816 	cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs);
817 	if (cli_context->channel == NULL) {
818 		unload_bs(cli_context, "Error in allocating channel",
819 			  -ENOMEM);
820 		return;
821 	}
822 
823 	switch (cli_context->action) {
824 	case CLI_SET_SUPER:
825 		spdk_bs_set_super(cli_context->bs, cli_context->superid,
826 				  set_super_cb, cli_context);
827 		break;
828 	case CLI_SHOW_BS:
829 		spdk_bs_get_super(cli_context->bs, show_bs_cb, cli_context);
830 		break;
831 	case CLI_CREATE_BLOB:
832 		spdk_bs_md_create_blob(cli_context->bs, blob_create_cb,
833 				       cli_context);
834 		break;
835 	case CLI_SET_XATTR:
836 	case CLI_REM_XATTR:
837 		spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid,
838 				     set_xattr_cb, cli_context);
839 		break;
840 	case CLI_SHOW_BLOB:
841 	case CLI_LIST_BLOBS:
842 		spdk_bs_md_iter_first(cli_context->bs, blob_iter_cb, cli_context);
843 
844 		break;
845 	case CLI_DUMP:
846 	case CLI_IMPORT:
847 		spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid,
848 				     dump_imp_open_cb, cli_context);
849 		break;
850 	case CLI_FILL:
851 		spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid,
852 				     fill_blob_cb, cli_context);
853 		break;
854 
855 	default:
856 		/* should never get here */
857 		exit(-1);
858 		break;
859 	}
860 }
861 
862 /*
863  * Load the blobstore.
864  */
865 static void
866 load_bs(struct cli_context_t *cli_context)
867 {
868 	struct spdk_bdev *bdev = NULL;
869 	struct spdk_bs_dev *bs_dev = NULL;
870 
871 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
872 	if (bdev == NULL) {
873 		printf("Could not find a bdev\n");
874 		spdk_app_stop(-1);
875 		return;
876 	}
877 
878 	bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL);
879 	if (bs_dev == NULL) {
880 		printf("Could not create blob bdev!!\n");
881 		spdk_app_stop(-1);
882 		return;
883 	}
884 
885 	spdk_bs_load(bs_dev, NULL, load_bs_cb, cli_context);
886 }
887 
888 /*
889  * Lists all the blobs on this blobstore.
890  */
891 static void
892 list_bdevs(struct cli_context_t *cli_context)
893 {
894 	struct spdk_bdev *bdev = NULL;
895 
896 	printf("\nList bdevs:\n");
897 
898 	bdev = spdk_bdev_first();
899 	if (bdev == NULL) {
900 		printf("Could not find a bdev\n");
901 	}
902 	while (bdev) {
903 		printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev));
904 		printf("\tbdev Product Name: %s\n",
905 		       spdk_bdev_get_product_name(bdev));
906 		bdev = spdk_bdev_next(bdev);
907 	}
908 
909 	printf("\n");
910 	if (cli_context->cli_mode == CLI_MODE_CMD) {
911 		spdk_app_stop(0);
912 	} else {
913 		cli_context->action = CLI_NONE;
914 		cli_start(cli_context, NULL);
915 	}
916 }
917 
918 /*
919  * Callback function for initializing a blob.
920  */
921 static void
922 bs_init_cb(void *cb_arg, struct spdk_blob_store *bs,
923 	   int bserrno)
924 {
925 	struct cli_context_t *cli_context = cb_arg;
926 
927 	if (bserrno) {
928 		unload_bs(cli_context, "Error in bs init callback",
929 			  bserrno);
930 		return;
931 	}
932 	cli_context->bs = bs;
933 	printf("blobstore init'd: (%p)\n", cli_context->bs);
934 
935 	unload_bs(cli_context, "", 0);
936 }
937 
938 /*
939  * Initialize a new blobstore.
940  */
941 static void
942 init_bs(struct cli_context_t *cli_context)
943 {
944 	struct spdk_bdev *bdev = NULL;
945 
946 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
947 	if (bdev == NULL) {
948 		printf("Could not find a bdev\n");
949 		spdk_app_stop(-1);
950 		return;
951 	}
952 	printf("Init blobstore using bdev Product Name: %s\n",
953 	       spdk_bdev_get_product_name(bdev));
954 
955 	cli_context->bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL);
956 	if (cli_context->bs_dev == NULL) {
957 		printf("Could not create blob bdev!!\n");
958 		spdk_app_stop(-1);
959 		return;
960 	}
961 
962 	spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb,
963 		     cli_context);
964 }
965 
966 /*
967  * Common cmd/option parser for command and shell modes.
968  */
969 static bool
970 cmd_parser(int argc, char **argv, struct cli_context_t *cli_context)
971 {
972 	int op;
973 	int cmd_chosen = 0;
974 	char resp;
975 
976 	while ((op = getopt(argc, argv, "b:c:d:f:hil:m:n:p:r:s:ST:Xx:")) != -1) {
977 		switch (op) {
978 		case 'b':
979 			if (strcmp(cli_context->bdev_name, "") == 0) {
980 				snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg);
981 			} else {
982 				printf("Current setting for -b is: %s\n", cli_context->bdev_name);
983 				usage(cli_context, "ERROR: -b option can only be set once.\n");
984 			}
985 			break;
986 		case 'c':
987 			if (cli_context->app_started == false) {
988 				cli_context->config_file = optarg;
989 			} else {
990 				usage(cli_context, "ERROR: -c option not valid during shell mode.\n");
991 			}
992 			break;
993 		case 'd':
994 			if (argv[optind] != NULL) {
995 				cmd_chosen++;
996 				cli_context->action = CLI_DUMP;
997 				cli_context->blobid = atoll(optarg);
998 				snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]);
999 			} else {
1000 				usage(cli_context, "ERROR: missing parameter.\n");
1001 			}
1002 			break;
1003 		case 'f':
1004 			if (argv[optind] != NULL) {
1005 				cmd_chosen++;
1006 				cli_context->action = CLI_FILL;
1007 				cli_context->blobid = atoll(optarg);
1008 				cli_context->fill_value = atoi(argv[optind]);
1009 			} else {
1010 				usage(cli_context, "ERROR: missing parameter.\n");
1011 			}
1012 			break;
1013 		case 'h':
1014 			cmd_chosen++;
1015 			cli_context->action = CLI_HELP;
1016 			break;
1017 		case 'i':
1018 			if (cli_context->cli_mode != CLI_MODE_SCRIPT) {
1019 				printf("Your entire blobstore will be destroyed. Are you sure? (y/n) ");
1020 				if (scanf("%c%*c", &resp)) {
1021 					if (resp == 'y' || resp == 'Y') {
1022 						cmd_chosen++;
1023 						cli_context->action = CLI_INIT_BS;
1024 					} else {
1025 						if (cli_context->cli_mode == CLI_MODE_CMD) {
1026 							spdk_app_stop(0);
1027 							return false;
1028 						}
1029 					}
1030 				}
1031 			} else {
1032 				cmd_chosen++;
1033 				cli_context->action = CLI_INIT_BS;
1034 			}
1035 			break;
1036 		case 'r':
1037 			if (argv[optind] != NULL) {
1038 				cmd_chosen++;
1039 				cli_context->action = CLI_REM_XATTR;
1040 				cli_context->blobid = atoll(optarg);
1041 				snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]);
1042 			} else {
1043 				usage(cli_context, "ERROR: missing parameter.\n");
1044 			}
1045 			break;
1046 		case 'l':
1047 			if (strcmp("bdevs", optarg) == 0) {
1048 				cmd_chosen++;
1049 				cli_context->action = CLI_LIST_BDEVS;
1050 			} else if (strcmp("blobs", optarg) == 0) {
1051 				cmd_chosen++;
1052 				cli_context->action = CLI_LIST_BLOBS;
1053 			} else {
1054 				usage(cli_context, "ERROR: invalid option for list\n");
1055 			}
1056 			break;
1057 		case 'm':
1058 			if (argv[optind] != NULL) {
1059 				cmd_chosen++;
1060 				cli_context->action = CLI_IMPORT;
1061 				cli_context->blobid = atoll(optarg);
1062 				snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]);
1063 			} else {
1064 				usage(cli_context, "ERROR: missing parameter.\n");
1065 			}
1066 			break;
1067 		case 'n':
1068 			cli_context->num_clusters = atoi(optarg);
1069 			if (cli_context->num_clusters > 0) {
1070 				cmd_chosen++;
1071 				cli_context->action = CLI_CREATE_BLOB;
1072 			} else {
1073 				usage(cli_context, "ERROR: invalid option for new\n");
1074 			}
1075 			break;
1076 		case 'p':
1077 			cmd_chosen++;
1078 			cli_context->action = CLI_SET_SUPER;
1079 			cli_context->superid = atoll(optarg);
1080 			break;
1081 		case 'S':
1082 			if (cli_context->cli_mode == CLI_MODE_CMD) {
1083 				cmd_chosen++;
1084 				cli_context->cli_mode = CLI_MODE_SHELL;
1085 			}
1086 			cli_context->action = CLI_NONE;
1087 			break;
1088 		case 's':
1089 			cmd_chosen++;
1090 			if (strcmp("bs", optarg) == 0) {
1091 				cli_context->action = CLI_SHOW_BS;
1092 			} else {
1093 				cli_context->action = CLI_SHOW_BLOB;
1094 				cli_context->blobid = atoll(optarg);
1095 			}
1096 			break;
1097 		case 'T':
1098 			if (cli_context->cli_mode == CLI_MODE_CMD) {
1099 				cmd_chosen++;
1100 				cli_context->cli_mode = CLI_MODE_SCRIPT;
1101 				if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) {
1102 					g_script.ignore_errors = true;
1103 				} else {
1104 					g_script.ignore_errors = false;
1105 				}
1106 				snprintf(cli_context->script_file, BUFSIZE, "%s", optarg);
1107 			} else {
1108 				cli_context->action = CLI_NONE;
1109 			}
1110 			break;
1111 		case 'X':
1112 			cmd_chosen++;
1113 			cli_context->action = CLI_SHELL_EXIT;
1114 			break;
1115 		case 'x':
1116 			if (argv[optind] != NULL || argv[optind + 1] != NULL) {
1117 				cmd_chosen++;
1118 				cli_context->action = CLI_SET_XATTR;
1119 				cli_context->blobid = atoll(optarg);
1120 				snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]);
1121 				snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]);
1122 			} else {
1123 				usage(cli_context, "ERROR: missing parameter.\n");
1124 			}
1125 			break;
1126 		default:
1127 			usage(cli_context, "ERROR: invalid option\n");
1128 		}
1129 		/* only one actual command can be done at a time */
1130 		if (cmd_chosen > 1) {
1131 			usage(cli_context, "Error: Please choose only one command\n");
1132 		}
1133 	}
1134 
1135 	if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) {
1136 		usage(cli_context, "Error: Please choose a command.\n");
1137 	}
1138 
1139 	/*
1140 	 * We don't check the local boolean because in some modes it will have been set
1141 	 * on and earlier command.
1142 	 */
1143 	if (strcmp(cli_context->bdev_name, "") == 0) {
1144 		usage(cli_context, "Error: -b option is required.\n");
1145 		cmd_chosen = 0;
1146 	}
1147 
1148 	/* in shell mode we'll call getopt multiple times so need to reset its index */
1149 	optind = 0;
1150 	return (cmd_chosen == 1);
1151 }
1152 
1153 /*
1154  * In script mode, we parsed a script file at startup and saved off a bunch of cmd
1155  * lines that we now parse with each run of cli_start so we us the same cmd parser
1156  * as cmd and shell modes.
1157  */
1158 static bool
1159 line_parser(struct cli_context_t *cli_context)
1160 {
1161 	bool cmd_chosen;
1162 	char *tok = NULL;
1163 	int blob_num = 0;
1164 	int start_idx = cli_context->argc;
1165 	int i;
1166 
1167 	printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]);
1168 	tok = strtok(g_script.cmdline[g_script.cmdline_idx], " ");
1169 	while (tok != NULL) {
1170 		/*
1171 		 * We support one replaceable token right now, a $Bn
1172 		 * represents the blobid that was created in position n
1173 		 * so fish this out now and use it here.
1174 		 */
1175 		cli_context->argv[cli_context->argc] = strdup(tok);
1176 		if (tok[0] == '$' && tok[1] == 'B') {
1177 			tok += 2;
1178 			blob_num = atoi(tok);
1179 			if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) {
1180 				cli_context->argv[cli_context->argc] =
1181 					realloc(cli_context->argv[cli_context->argc], BUFSIZE);
1182 				if (cli_context->argv[cli_context->argc] == NULL) {
1183 					printf("ERROR: unable to realloc memory\n");
1184 					spdk_app_stop(-1);
1185 				}
1186 				if (g_script.blobid[blob_num] == 0) {
1187 					printf("ERROR: There is no blob for $B%d\n",
1188 					       blob_num);
1189 				}
1190 				snprintf(cli_context->argv[cli_context->argc], BUFSIZE,
1191 					 "%" PRIu64, g_script.blobid[blob_num]);
1192 			} else {
1193 				printf("ERROR: Invalid token or exceeded max blobs of %d\n",
1194 				       MAX_SCRIPT_BLOBS);
1195 			}
1196 		}
1197 		cli_context->argc++;
1198 		tok = strtok(NULL, " ");
1199 	}
1200 
1201 	/* call parse cmd line with user input as args */
1202 	cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context);
1203 
1204 	/* free strdup memory and reset arg count for next shell interaction */
1205 	for (i = start_idx; i < cli_context->argc; i++) {
1206 		free(cli_context->argv[i]);
1207 		cli_context->argv[i] = NULL;
1208 	}
1209 	cli_context->argc = 1;
1210 
1211 	g_script.cmdline_idx++;
1212 	assert(g_script.cmdline_idx < MAX_SCRIPT_LINES);
1213 
1214 	if (cmd_chosen == false) {
1215 		printf("ERROR: Invalid script line starting with: %s\n\n",
1216 		       g_script.cmdline[g_script.cmdline_idx - 1]);
1217 		if (g_script.ignore_errors == false) {
1218 			printf("** Aborting **\n");
1219 			cli_context->action = CLI_SHELL_EXIT;
1220 			cmd_chosen = true;
1221 			unload_bs(cli_context, "", 0);
1222 		} else {
1223 			printf("** Skipping **\n");
1224 		}
1225 	}
1226 
1227 	return cmd_chosen;
1228 }
1229 
1230 /*
1231  * For script mode, we read a series of commands from a text file and store them
1232  * in a global struct. That, along with the cli_mode that tells us we're in
1233  * script mode is what feeds the rest of the app in the same way as is it were
1234  * getting commands from shell mode.
1235  */
1236 static void
1237 parse_script(struct cli_context_t *cli_context)
1238 {
1239 	FILE *fp = NULL;
1240 	size_t bufsize = BUFSIZE;
1241 	int64_t bytes_in = 0;
1242 	int i = 0;
1243 
1244 	/* initialize global script values */
1245 	for (i = 0; i < MAX_SCRIPT_BLOBS; i++) {
1246 		g_script.blobid[i] = 0;
1247 	}
1248 	g_script.blobid_idx = 0;
1249 	g_script.cmdline_idx = 0;
1250 	i = 0;
1251 
1252 	fp = fopen(cli_context->script_file, "r");
1253 	if (fp == NULL) {
1254 		printf("ERROR: unable to open script: %s\n",
1255 		       cli_context->script_file);
1256 		cli_cleanup(cli_context);
1257 		exit(-1);
1258 	}
1259 
1260 	do {
1261 		bytes_in = getline(&g_script.cmdline[i], &bufsize, fp);
1262 		if (bytes_in > 0) {
1263 			/* replace newline with null */
1264 			spdk_str_chomp(g_script.cmdline[i]);
1265 
1266 			/* ignore comments */
1267 			if (g_script.cmdline[i][0] != '#') {
1268 				i++;
1269 			}
1270 		}
1271 	} while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1);
1272 	fclose(fp);
1273 
1274 	/* add an exit cmd in case they didn't */
1275 	g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE);
1276 	if (g_script.cmdline[i] == NULL)  {
1277 		int j;
1278 
1279 		for (j = 0; j < i; j++) {
1280 			free(g_script.cmdline[j]);
1281 			g_script.cmdline[j] = NULL;
1282 		}
1283 		unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0);
1284 	}
1285 	snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X");
1286 	g_script.max_index = i;
1287 }
1288 
1289 /*
1290  * Provides for a shell interface as opposed to one shot command line.
1291  */
1292 static bool
1293 cli_shell(void *arg1, void *arg2)
1294 {
1295 	struct cli_context_t *cli_context = arg1;
1296 	char *line = NULL;
1297 	ssize_t buf_size = 0;
1298 	ssize_t bytes_in = 0;
1299 	ssize_t tok_len = 0;
1300 	char *tok = NULL;
1301 	bool cmd_chosen = false;
1302 	int start_idx = cli_context->argc;
1303 	int i;
1304 
1305 	printf("blob> ");
1306 	bytes_in = getline(&line, &buf_size, stdin);
1307 
1308 	/* parse input and update cli_context so we can use common option parser */
1309 	if (bytes_in > 0) {
1310 		tok = strtok(line, " ");
1311 	}
1312 	while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) {
1313 		cli_context->argv[cli_context->argc] = strdup(tok);
1314 		tok_len = strlen(tok);
1315 		cli_context->argc++;
1316 		tok = strtok(NULL, " ");
1317 	}
1318 
1319 	/* replace newline on last arg with null */
1320 	if (tok_len) {
1321 		spdk_str_chomp(cli_context->argv[cli_context->argc - 1]);
1322 	}
1323 
1324 	/* call parse cmd line with user input as args */
1325 	cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context);
1326 
1327 	/* free strdup mem & reset arg count for next shell interaction */
1328 	for (i = start_idx; i < cli_context->argc; i++) {
1329 		free(cli_context->argv[i]);
1330 		cli_context->argv[i] = NULL;
1331 	}
1332 	cli_context->argc = 1;
1333 
1334 	free(line);
1335 
1336 	return cmd_chosen;
1337 }
1338 
1339 /*
1340  * This is the function we pass into the SPDK framework that gets
1341  * called first.
1342  */
1343 static void
1344 cli_start(void *arg1, void *arg2)
1345 {
1346 	struct cli_context_t *cli_context = arg1;
1347 
1348 	/*
1349 	 * If we're in script mode, we already have a list of commands so
1350 	 * just need to pull them out one at a time and process them.
1351 	 */
1352 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
1353 		while (line_parser(cli_context) == false);
1354 	}
1355 
1356 	/*
1357 	 * The initial cmd line options are parsed once before this function is
1358 	 * called so if there is no action, we're in shell mode and will loop
1359 	 * here until a a valid option is parsed and returned.
1360 	 */
1361 	if (cli_context->action == CLI_NONE) {
1362 		while (cli_shell(cli_context, NULL) == false);
1363 	}
1364 
1365 	/* Decide what to do next based on cmd line parsing. */
1366 	switch (cli_context->action) {
1367 	case CLI_SET_SUPER:
1368 	case CLI_SHOW_BS:
1369 	case CLI_CREATE_BLOB:
1370 	case CLI_SET_XATTR:
1371 	case CLI_REM_XATTR:
1372 	case CLI_SHOW_BLOB:
1373 	case CLI_LIST_BLOBS:
1374 	case CLI_DUMP:
1375 	case CLI_IMPORT:
1376 	case CLI_FILL:
1377 		load_bs(cli_context);
1378 		break;
1379 	case CLI_INIT_BS:
1380 		init_bs(cli_context);
1381 		break;
1382 	case CLI_LIST_BDEVS:
1383 		list_bdevs(cli_context);
1384 		break;
1385 	case CLI_SHELL_EXIT:
1386 		/*
1387 		 * Because shell mode reuses cmd mode functions, the blobstore
1388 		 * is loaded/unloaded with every action so we just need to
1389 		 * stop the framework. For this app there's no need to optimize
1390 		 * and keep the blobstore open while the app is in shell mode.
1391 		 */
1392 		spdk_app_stop(0);
1393 		break;
1394 	case CLI_HELP:
1395 		usage(cli_context, "");
1396 		unload_complete(cli_context, 0);
1397 		break;
1398 	default:
1399 		/* should never get here */
1400 		exit(-1);
1401 		break;
1402 	}
1403 }
1404 
1405 int
1406 main(int argc, char **argv)
1407 {
1408 	struct spdk_app_opts opts = {};
1409 	struct cli_context_t *cli_context = NULL;
1410 	bool cmd_chosen;
1411 	int rc = 0;
1412 
1413 	if (argc < 2) {
1414 		usage(cli_context, "ERROR: Invalid option\n");
1415 		exit(-1);
1416 	}
1417 
1418 	cli_context = calloc(1, sizeof(struct cli_context_t));
1419 	if (cli_context == NULL) {
1420 		printf("ERROR: could not allocate context structure\n");
1421 		exit(-1);
1422 	}
1423 
1424 	/* default to CMD mode until we've parsed the first parms */
1425 	cli_context->cli_mode = CLI_MODE_CMD;
1426 	cli_context->argv[0] = strdup(argv[0]);
1427 	cli_context->argc = 1;
1428 
1429 	/* parse command line */
1430 	cmd_chosen = cmd_parser(argc, argv, cli_context);
1431 	free(cli_context->argv[0]);
1432 	cli_context->argv[0] = NULL;
1433 	if (cmd_chosen == false) {
1434 		cli_cleanup(cli_context);
1435 		exit(-1);
1436 	}
1437 
1438 	/* after displaying help, just exit */
1439 	if (cli_context->action == CLI_HELP) {
1440 		usage(cli_context, "");
1441 		cli_cleanup(cli_context);
1442 		exit(-1);
1443 	}
1444 
1445 	/* if they don't supply a conf name, use the default */
1446 	if (!cli_context->config_file) {
1447 		cli_context->config_file = program_conf;
1448 	}
1449 
1450 	/* if the config file doesn't exist, tell them how to make one */
1451 	if (access(cli_context->config_file, F_OK) == -1) {
1452 		printf("Error: No config file found.\n");
1453 		printf("To create a config file named 'blobcli.conf' for your NVMe device:\n");
1454 		printf("   <path to spdk>/scripts/gen_nvme.sh > blobcli.conf\n");
1455 		printf("and then re-run the cli tool.\n");
1456 		exit(-1);
1457 	}
1458 
1459 	/*
1460 	 * For script mode we keep a bunch of stuff in a global since
1461 	 * none if it is passed back and forth to SPDK.
1462 	 */
1463 	if (cli_context->cli_mode == CLI_MODE_SCRIPT) {
1464 		/*
1465 		 * Now we'll build up the global which will direct this run of the app
1466 		 * as it will have a list (g_script) of all of the commands line by
1467 		 * line as if they were typed in on the shell at cmd line.
1468 		 */
1469 		parse_script(cli_context);
1470 	}
1471 
1472 	/* Set default values in opts struct along with name and conf file. */
1473 	spdk_app_opts_init(&opts);
1474 	opts.name = "blobcli";
1475 	opts.config_file = cli_context->config_file;
1476 
1477 	cli_context->app_started = true;
1478 	rc = spdk_app_start(&opts, cli_start, cli_context, NULL);
1479 	if (rc) {
1480 		printf("ERROR!\n");
1481 	}
1482 
1483 	/* Free up memory that we allocated */
1484 	cli_cleanup(cli_context);
1485 
1486 	/* Gracefully close out all of the SPDK subsystems. */
1487 	spdk_app_fini();
1488 	return rc;
1489 }
1490