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