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