xref: /spdk/examples/blob/cli/blobcli.c (revision 7e846d2bb99838a21b042dd2db1d0e36eb17f95c)
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 
44 /*
45  * This is not a public header file, but the CLI does expose
46  * some internals of blobstore for dev/debug puposes so we
47  * include it here.
48  */
49 #include "../lib/blob/blobstore.h"
50 
51 static const char *program_name = "blobcli";
52 static const char *program_conf = "blobcli.conf";
53 static const char *bdev_name = "Nvme0n1";
54 
55 enum cli_action_type {
56 	CLI_IMPORT,
57 	CLI_DUMP,
58 	CLI_FILL,
59 	CLI_REM_XATTR,
60 	CLI_SET_XATTR,
61 	CLI_SET_SUPER,
62 	CLI_SHOW_BS,
63 	CLI_SHOW_BLOB,
64 	CLI_CREATE_BLOB,
65 	CLI_LIST_BDEVS,
66 	CLI_LIST_BLOBS,
67 	CLI_INIT_BS
68 };
69 #define BUFSIZE 255
70 
71 /* todo, scrub this as there may be some extra junk in here picked up along the way... */
72 struct cli_context_t {
73 	struct spdk_blob_store *bs;
74 	struct spdk_blob *blob;
75 	spdk_blob_id blobid;
76 	spdk_blob_id superid;
77 	struct spdk_io_channel *channel;
78 	uint8_t *buff;
79 	uint64_t page_size;
80 	uint64_t page_count;
81 	uint64_t blob_pages;
82 	uint64_t bytes_so_far;
83 	FILE *fp;
84 	enum cli_action_type action;
85 	char key[BUFSIZE + 1];
86 	char value[BUFSIZE + 1];
87 	char file[BUFSIZE + 1];
88 	uint64_t filesize;
89 	int fill_value;
90 	const char *bdev_name;
91 	int rc;
92 	int num_clusters;
93 	void (*next_func)(void *arg1, struct spdk_blob_store *bs, int bserrno);
94 };
95 
96 /*
97  * Prints usage and relevant error message.
98  */
99 static void
100 usage(char *msg)
101 {
102 	if (msg) {
103 		printf("%s", msg);
104 	}
105 	printf("Version %s\n", SPDK_VERSION_STRING);
106 	printf("Usage: %s [-c SPDK config_file] Command\n", program_name);
107 	printf("\n%s is a command line tool for interacting with blobstore\n",
108 	       program_name);
109 	printf("on the underlying device specified in the conf file passed\n");
110 	printf("in as a command line option.\n");
111 	printf("\nCommands include:\n");
112 	printf("\t-i - initialize a blobstore\n");
113 	printf("\t-l bdevs | blobs - list either available bdevs or existing blobs\n");
114 	printf("\t-n <# clusters> - create new blob\n");
115 	printf("\t-p <blobid> - set the superblob to the ID provided\n");
116 	printf("\t-s <blobid> | bs - show blob info or blobstore info\n");
117 	printf("\t-x <blobid> name value - set xattr name/value pair\n");
118 	printf("\t-r <blobid> name - remove xattr name/value pair\n");
119 	printf("\t-f <blobid> value - fill a blob with a decimal value\n");
120 	printf("\t-d <blobid> filename - dump contents of a blob to a file\n");
121 	printf("\t-m <blobid> filename - import contents of a file to a blob\n");
122 	printf("\n");
123 }
124 
125 /*
126  * Free up memory that we allocated.
127  */
128 static void
129 cli_cleanup(struct cli_context_t *cli_context)
130 {
131 	if (cli_context->buff) {
132 		spdk_dma_free(cli_context->buff);
133 	}
134 	if (cli_context->channel) {
135 		spdk_bs_free_io_channel(cli_context->channel);
136 	}
137 	free(cli_context);
138 }
139 
140 /*
141  * Callback routine for the blobstore unload.
142  */
143 static void
144 unload_complete(void *cb_arg, int bserrno)
145 {
146 	struct cli_context_t *cli_context = cb_arg;
147 
148 	if (bserrno) {
149 		printf("Error %d unloading the bobstore\n", bserrno);
150 		cli_context->rc = bserrno;
151 	}
152 
153 	spdk_app_stop(cli_context->rc);
154 }
155 
156 /*
157  * Unload the blobstore.
158  */
159 static void
160 unload_bs(struct cli_context_t *cli_context, char *msg, int bserrno)
161 {
162 	if (bserrno) {
163 		printf("%s (err %d)\n", msg, bserrno);
164 		cli_context->rc = bserrno;
165 	}
166 	if (cli_context->bs) {
167 		spdk_bs_unload(cli_context->bs, unload_complete, cli_context);
168 	} else {
169 		spdk_app_stop(bserrno);
170 	}
171 }
172 
173 /*
174  * Callback for closing a blob.
175  */
176 static void
177 close_cb(void *arg1, int bserrno)
178 {
179 	struct cli_context_t *cli_context = arg1;
180 
181 	if (bserrno) {
182 		unload_bs(cli_context, "Error in close callback",
183 			  bserrno);
184 		return;
185 	}
186 	unload_bs(cli_context, "", 0);
187 }
188 
189 /*
190  * Callback function for sync'ing metadata.
191  */
192 static void
193 sync_complete(void *arg1, int bserrno)
194 {
195 	struct cli_context_t *cli_context = arg1;
196 
197 	if (bserrno) {
198 		unload_bs(cli_context, "Error in sync callback",
199 			  bserrno);
200 		return;
201 	}
202 
203 	spdk_bs_md_close_blob(&cli_context->blob, close_cb,
204 			      cli_context);
205 }
206 
207 /*
208  * Callback function for opening a blob after creating.
209  */
210 static void
211 open_now_resize(void *cb_arg, struct spdk_blob *blob, int bserrno)
212 {
213 	struct cli_context_t *cli_context = cb_arg;
214 	int rc = 0;
215 	uint64_t total = 0;
216 
217 	if (bserrno) {
218 		unload_bs(cli_context, "Error in open completion",
219 			  bserrno);
220 		return;
221 	}
222 	cli_context->blob = blob;
223 
224 	rc = spdk_bs_md_resize_blob(cli_context->blob,
225 				    cli_context->num_clusters);
226 	if (rc) {
227 		unload_bs(cli_context, "Error in blob resize",
228 			  bserrno);
229 		return;
230 	}
231 
232 	total = spdk_blob_get_num_clusters(cli_context->blob);
233 	printf("blob now has USED clusters of %" PRIu64 "\n",
234 	       total);
235 
236 	/*
237 	 * Always a good idea to sync after MD changes or the changes
238 	 * may be lost if things aren't closed cleanly.
239 	 */
240 	spdk_bs_md_sync_blob(cli_context->blob, sync_complete,
241 			     cli_context);
242 }
243 
244 /*
245  * Callback function for creating a blob.
246  */
247 static void
248 blob_create_complete(void *arg1, spdk_blob_id blobid, int bserrno)
249 {
250 	struct cli_context_t *cli_context = arg1;
251 
252 	if (bserrno) {
253 		unload_bs(cli_context, "Error in blob create callback",
254 			  bserrno);
255 		return;
256 	}
257 
258 	cli_context->blobid = blobid;
259 	printf("New blob id %" PRIu64 "\n", cli_context->blobid);
260 
261 	/* We have to open the blob before we can do things like resize. */
262 	spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid,
263 			     open_now_resize, cli_context);
264 }
265 
266 /*
267  * Callback for get_super where we'll continue on to show blobstore info.
268  */
269 static void
270 show_bs(void *arg1, spdk_blob_id blobid, int bserrno)
271 {
272 	struct cli_context_t *cli_context = arg1;
273 	uint64_t val;
274 	struct spdk_bdev *bdev = NULL;
275 
276 	if (bserrno && bserrno != -ENOENT) {
277 		unload_bs(cli_context, "Error in get_super callback",
278 			  bserrno);
279 		return;
280 	}
281 	cli_context->superid = blobid;
282 
283 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
284 	if (bdev == NULL) {
285 		unload_bs(cli_context, "Error w/bdev in get_super callback",
286 			  bserrno);
287 		return;
288 	}
289 
290 	printf("Blobstore Public Info:\n");
291 	printf("\tUsing bdev Product Name: %s\n",
292 	       spdk_bdev_get_product_name(bdev));
293 	printf("\tAPI Version: %d\n", SPDK_BS_VERSION);
294 
295 	if (bserrno != -ENOENT) {
296 		printf("\tsuper blob ID: %" PRIu64 "\n", cli_context->superid);
297 	} else {
298 		printf("\tsuper blob ID: none assigned\n");
299 	}
300 
301 	val = spdk_bs_get_page_size(cli_context->bs);
302 	printf("\tpage size: %" PRIu64 "\n", val);
303 
304 	val = spdk_bs_get_cluster_size(cli_context->bs);
305 	printf("\tcluster size: %" PRIu64 "\n", val);
306 
307 	val = spdk_bs_free_cluster_count(cli_context->bs);
308 	printf("\t# free clusters: %" PRIu64 "\n", val);
309 
310 	/*
311 	 * Private info isn't accessible via the public API but
312 	 * may be useful for debug of blobstore based applications.
313 	 */
314 	printf("\nBlobstore Private Info:\n");
315 	printf("\tMetadata start (pages): %" PRIu64 "\n",
316 	       cli_context->bs->md_start);
317 	printf("\tMetadata length (pages): %d \n",
318 	       cli_context->bs->md_len);
319 
320 	unload_bs(cli_context, "", 0);
321 }
322 
323 /*
324  * Load callback where we'll get the super blobid next.
325  */
326 static void
327 get_super_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
328 {
329 	struct cli_context_t *cli_context = arg1;
330 
331 	if (bserrno) {
332 		unload_bs(cli_context, "Error in load blob callback",
333 			  bserrno);
334 		return;
335 	}
336 	cli_context->bs = bs;
337 
338 	spdk_bs_get_super(cli_context->bs, show_bs, cli_context);
339 }
340 
341 /*
342  * Callback for load bs where we'll continue on to create a blob.
343  */
344 static void
345 create_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
346 {
347 	struct cli_context_t *cli_context = arg1;
348 
349 	if (bserrno) {
350 		unload_bs(cli_context, "Error in load callback",
351 			  bserrno);
352 		return;
353 	}
354 	cli_context->bs = bs;
355 
356 	spdk_bs_md_create_blob(cli_context->bs, blob_create_complete,
357 			       cli_context);
358 }
359 
360 /*
361  * Show detailed info about a particular blob.
362  */
363 static void
364 show_blob(struct cli_context_t *cli_context)
365 {
366 	uint64_t val;
367 	struct spdk_xattr_names *names;
368 	const void *value;
369 	size_t value_len;
370 	char data[256];
371 	unsigned int i;
372 
373 	printf("Blob Public Info:\n");
374 
375 	printf("blob ID: %" PRIu64 "\n", cli_context->blobid);
376 
377 	val = spdk_blob_get_num_clusters(cli_context->blob);
378 	printf("# of clusters: %" PRIu64 "\n", val);
379 
380 	printf("# of bytes: %" PRIu64 "\n",
381 	       val * spdk_bs_get_cluster_size(cli_context->bs));
382 
383 	val = spdk_blob_get_num_pages(cli_context->blob);
384 	printf("# of pages: %" PRIu64 "\n", val);
385 
386 	spdk_bs_md_get_xattr_names(cli_context->blob, &names);
387 
388 	printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names));
389 	printf("xattrs:\n");
390 	for (i = 0; i < spdk_xattr_names_get_count(names); i++) {
391 		spdk_bs_md_get_xattr_value(cli_context->blob,
392 					   spdk_xattr_names_get_name(names, i),
393 					   &value, &value_len);
394 		if ((value_len + 1) > sizeof(data)) {
395 			printf("FYI: adjusting size of xattr due to CLI limits.\n");
396 			value_len = sizeof(data) - 1;
397 		}
398 		memcpy(&data, value, value_len);
399 		data[value_len] = '\0';
400 		printf("\n(%d) Name:%s\n", i,
401 		       spdk_xattr_names_get_name(names, i));
402 		printf("(%d) Value:\n", i);
403 		spdk_trace_dump(stdout, "", value, value_len);
404 	}
405 
406 	/*
407 	 * Private info isn't accessible via the public API but
408 	 * may be useful for debug of blobstore based applications.
409 	 */
410 	printf("\nBlob Private Info:\n");
411 	switch (cli_context->blob->state) {
412 	case SPDK_BLOB_STATE_DIRTY:
413 		printf("state: DIRTY\n");
414 		break;
415 	case SPDK_BLOB_STATE_CLEAN:
416 		printf("state: CLEAN\n");
417 		break;
418 	case SPDK_BLOB_STATE_LOADING:
419 		printf("state: LOADING\n");
420 		break;
421 	case SPDK_BLOB_STATE_SYNCING:
422 		printf("state: SYNCING\n");
423 		break;
424 	default:
425 		printf("state: UNKNOWN\n");
426 		break;
427 	}
428 	printf("open ref count: %d\n",
429 	       cli_context->blob->open_ref);
430 }
431 
432 /*
433  * Callback for getting the first blob.
434  */
435 static void
436 blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno)
437 {
438 	struct cli_context_t *cli_context = arg1;
439 
440 	if (bserrno) {
441 		if (bserrno == -ENOENT) {
442 			/* this simply means there are no more blobs */
443 			unload_bs(cli_context, "", 0);
444 		} else {
445 			unload_bs(cli_context, "Error in blob iter callback",
446 				  bserrno);
447 		}
448 		return;
449 	}
450 
451 	if (cli_context->action == CLI_LIST_BLOBS) {
452 		/* just listing blobs */
453 		printf("Found blob with ID# %" PRIu64 "\n",
454 		       spdk_blob_get_id(blob));
455 	} else if (spdk_blob_get_id(blob) == cli_context->blobid) {
456 		/*
457 		 * Found the blob we're looking for, but we need to finish
458 		 * iterating even after showing the info so that internally
459 		 * the blobstore logic will close the blob. Or we could
460 		 * chose to close it now, either way.
461 		 */
462 		cli_context->blob = blob;
463 		show_blob(cli_context);
464 	}
465 
466 	spdk_bs_md_iter_next(cli_context->bs, &blob, blob_iter_cb,
467 			     cli_context);
468 }
469 
470 /*
471  * Callback for load bs where we'll continue on to list all blobs.
472  */
473 static void
474 list_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
475 {
476 	struct cli_context_t *cli_context = arg1;
477 
478 	if (bserrno) {
479 		unload_bs(cli_context, "Error in load callback",
480 			  bserrno);
481 		return;
482 	}
483 	cli_context->bs = bs;
484 
485 	if (cli_context->action == CLI_LIST_BLOBS) {
486 		printf("\nList BLOBS:\n");
487 	}
488 
489 	spdk_bs_md_iter_first(cli_context->bs, blob_iter_cb, cli_context);
490 }
491 
492 /*
493  * Callback for setting the super blob ID.
494  */
495 static void
496 set_super_cb(void *arg1, int bserrno)
497 {
498 	struct cli_context_t *cli_context = arg1;
499 
500 	if (bserrno) {
501 		unload_bs(cli_context, "Error in set_super callback",
502 			  bserrno);
503 		return;
504 	}
505 
506 	printf("Super Blob ID has been set.\n");
507 	unload_bs(cli_context, "", 0);
508 }
509 
510 /*
511  * Callback for load bs where we'll continue on to set the super blob.
512  */
513 static void
514 set_super_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
515 {
516 	struct cli_context_t *cli_context = arg1;
517 
518 	if (bserrno) {
519 		unload_bs(cli_context, "Error in load callback",
520 			  bserrno);
521 		return;
522 	}
523 	cli_context->bs = bs;
524 
525 	spdk_bs_set_super(cli_context->bs, cli_context->superid,
526 			  set_super_cb, cli_context);
527 }
528 
529 /*
530  * Callback for set_xattr_open where we set or delete xattrs.
531  */
532 static void
533 set_xattr(void *cb_arg, struct spdk_blob *blob, int bserrno)
534 {
535 	struct cli_context_t *cli_context = cb_arg;
536 
537 	if (bserrno) {
538 		unload_bs(cli_context, "Error in blob open callback",
539 			  bserrno);
540 		return;
541 	}
542 	cli_context->blob = blob;
543 
544 	if (cli_context->action == CLI_SET_XATTR) {
545 		spdk_blob_md_set_xattr(cli_context->blob,
546 				       cli_context->key,
547 				       cli_context->value,
548 				       strlen(cli_context->value) + 1);
549 		printf("Xattr has been set.\n");
550 	} else {
551 		spdk_blob_md_remove_xattr(cli_context->blob,
552 					  cli_context->key);
553 		printf("Xattr has been removed.\n");
554 	}
555 
556 	spdk_bs_md_sync_blob(cli_context->blob, sync_complete,
557 			     cli_context);
558 }
559 
560 /*
561  * Callback for load bs where we'll continue on to set/del an xattr.
562  */
563 static void
564 xattr_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
565 {
566 	struct cli_context_t *cli_context = arg1;
567 
568 	if (bserrno) {
569 		unload_bs(cli_context, "Error in load callback",
570 			  bserrno);
571 		return;
572 	}
573 	cli_context->bs = bs;
574 
575 	spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid,
576 			     set_xattr, cli_context);
577 }
578 
579 /*
580  * Callback function for reading a blob for dumping to a file.
581  */
582 static void
583 read_dump_complete(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, 1, cli_context->page_size,
596 			       cli_context->fp);
597 	if (bytes_written != cli_context->page_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->page_count < cli_context->blob_pages) {
606 		/* perform another read */
607 		spdk_bs_io_read_blob(cli_context->blob, cli_context->channel,
608 				     cli_context->buff, cli_context->page_count,
609 				     1, read_dump_complete, cli_context);
610 	} else {
611 		/* done reading */
612 		printf("\nFile write complete.\n");
613 		fclose(cli_context->fp);
614 		spdk_bs_md_close_blob(&cli_context->blob, close_cb,
615 				      cli_context);
616 	}
617 }
618 
619 /*
620  * Callback for write completion on the import of a file to a blob.
621  */
622 static void
623 write_imp_complete(void *arg1, int bserrno)
624 {
625 	struct cli_context_t *cli_context = arg1;
626 	uint64_t bytes_read;
627 
628 	if (bserrno) {
629 		fclose(cli_context->fp);
630 		unload_bs(cli_context, "Error in write completion",
631 			  bserrno);
632 		return;
633 	}
634 
635 	if (cli_context->bytes_so_far < cli_context->filesize) {
636 		/* perform another file read */
637 		bytes_read = fread(cli_context->buff, 1,
638 				   cli_context->page_size,
639 				   cli_context->fp);
640 		cli_context->bytes_so_far += bytes_read;
641 
642 		/* if this read is < 1 page, fill with 0s */
643 		if (bytes_read < cli_context->page_size) {
644 			uint8_t *offset = cli_context->buff + bytes_read;
645 			memset(offset, 0,
646 			       cli_context->page_size - bytes_read);
647 		}
648 	} else {
649 		/*
650 		 * Done reading the file, fill the rest of the blob with 0s,
651 		 * yeah we're memsetting the same page over and over here
652 		 */
653 		memset(cli_context->buff, 0, cli_context->page_size);
654 	}
655 	if (++cli_context->page_count < cli_context->blob_pages) {
656 		printf(".");
657 		spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
658 				      cli_context->buff, cli_context->page_count,
659 				      1, write_imp_complete, cli_context);
660 	} else {
661 		/* done writing */
662 		printf("\nBlob import complete.\n");
663 		fclose(cli_context->fp);
664 		spdk_bs_md_close_blob(&cli_context->blob, close_cb,
665 				      cli_context);
666 	}
667 }
668 
669 /*
670  * Callback for open blobs where we'll continue on dump a blob to a file or
671  * import a file to a blob. For dump, the resulting file will always be the
672  * full size of the blob.  For import, the blob will fill with the file
673  * contents first and then 0 out the rest of the blob.
674  */
675 static void
676 dmpimp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno)
677 {
678 	struct cli_context_t *cli_context = cb_arg;
679 
680 	if (bserrno) {
681 		unload_bs(cli_context, "Error in blob open callback",
682 			  bserrno);
683 		return;
684 	}
685 	cli_context->blob = blob;
686 	cli_context->page_size = spdk_bs_get_page_size(cli_context->bs);
687 	cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs);
688 	if (cli_context->channel == NULL) {
689 		unload_bs(cli_context, "Error in allocating channel",
690 			  -ENOMEM);
691 		return;
692 	}
693 
694 	/*
695 	 * We'll transfer just one page at a time to keep the buffer
696 	 * small. This could be bigger of course.
697 	 */
698 	cli_context->buff = spdk_dma_malloc(cli_context->page_size,
699 					    0x1000, NULL);
700 	if (cli_context->buff == NULL) {
701 		unload_bs(cli_context, "Error in allocating memory",
702 			  -ENOMEM);
703 		return;
704 	}
705 	printf("Working");
706 	cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob);
707 	cli_context->page_count = 0;
708 	if (cli_context->action == CLI_DUMP) {
709 		cli_context->fp = fopen(cli_context->file , "w");
710 
711 		/* read a page of data from the blob */
712 		spdk_bs_io_read_blob(cli_context->blob, cli_context->channel,
713 				     cli_context->buff, cli_context->page_count,
714 				     1, read_dump_complete, cli_context);
715 	} else {
716 		cli_context->fp = fopen(cli_context->file , "r");
717 
718 		/* get the filesize then rewind read a page 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, 1,
723 						  cli_context->page_size,
724 						  cli_context->fp);
725 
726 		/* if the file is < a page, fill the rest with 0s */
727 		if (cli_context->filesize < cli_context->page_size) {
728 			uint8_t *offset =
729 				cli_context->buff + cli_context->filesize;
730 
731 			memset(offset, 0,
732 			       cli_context->page_size - cli_context->filesize);
733 		}
734 
735 		spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
736 				      cli_context->buff, cli_context->page_count,
737 				      1, write_imp_complete, cli_context);
738 	}
739 }
740 
741 /*
742  * Callback for load bs where we'll continue on dump a blob to a file or
743  * import a file to a blob.
744  */
745 static void
746 dmpimp_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
747 {
748 	struct cli_context_t *cli_context = arg1;
749 
750 	if (bserrno) {
751 		unload_bs(cli_context, "Error in load callback",
752 			  bserrno);
753 		return;
754 	}
755 	cli_context->bs = bs;
756 
757 	spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid,
758 			     dmpimp_open_cb, cli_context);
759 }
760 
761 /*
762  * Callback function for writing a specific pattern to page 0.
763  */
764 static void
765 write_complete(void *arg1, int bserrno)
766 {
767 	struct cli_context_t *cli_context = arg1;
768 
769 	if (bserrno) {
770 		unload_bs(cli_context, "Error in write completion",
771 			  bserrno);
772 		return;
773 	}
774 	printf(".");
775 	if (++cli_context->page_count < cli_context->blob_pages) {
776 		spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
777 				      cli_context->buff, cli_context->page_count,
778 				      1, write_complete, cli_context);
779 	} else {
780 		/* done writing */
781 		printf("\nBlob fill complete.\n");
782 		spdk_bs_md_close_blob(&cli_context->blob, close_cb,
783 				      cli_context);
784 	}
785 
786 }
787 
788 /*
789  * function to fill a blob with a value.
790  */
791 static void
792 fill_blob(void *cb_arg, struct spdk_blob *blob, int bserrno)
793 {
794 	struct cli_context_t *cli_context = cb_arg;
795 
796 	if (bserrno) {
797 		unload_bs(cli_context, "Error in blob open callback",
798 			  bserrno);
799 		return;
800 	}
801 	cli_context->blob = blob;
802 	cli_context->page_count = 0;
803 	cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob);
804 	cli_context->buff = spdk_dma_malloc(cli_context->page_size,
805 					    0x1000, NULL);
806 	if (cli_context->buff == NULL) {
807 		unload_bs(cli_context, "Error in allocating memory",
808 			  -ENOMEM);
809 		return;
810 	}
811 
812 	memset(cli_context->buff, cli_context->fill_value,
813 	       cli_context->page_size);
814 	printf("\n");
815 	spdk_bs_io_write_blob(cli_context->blob, cli_context->channel,
816 			      cli_context->buff,
817 			      0, 1, write_complete, cli_context);
818 }
819 
820 /*
821  * Callback for load bs where we'll continue on to fill a blob.
822  */
823 static void
824 fill_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno)
825 {
826 	struct cli_context_t *cli_context = arg1;
827 
828 	if (bserrno) {
829 		unload_bs(cli_context, "Error in load callback",
830 			  bserrno);
831 		return;
832 	}
833 	cli_context->bs = bs;
834 	cli_context->page_size = spdk_bs_get_page_size(cli_context->bs);
835 	cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs);
836 	if (cli_context->channel == NULL) {
837 		unload_bs(cli_context, "Error in allocating channel",
838 			  -ENOMEM);
839 		return;
840 	}
841 
842 	spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid,
843 			     fill_blob, cli_context);
844 }
845 
846 /*
847  * Multiple actions require us to open the bs first. A function pointer
848  * setup earlier will direct the callback accordingly.
849  */
850 static void
851 load_bs(struct cli_context_t *cli_context)
852 {
853 	struct spdk_bdev *bdev = NULL;
854 	struct spdk_bs_dev *bs_dev = NULL;
855 
856 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
857 	if (bdev == NULL) {
858 		printf("Could not find a bdev\n");
859 		spdk_app_stop(-1);
860 		return;
861 	}
862 
863 	bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL);
864 	if (bs_dev == NULL) {
865 		printf("Could not create blob bdev!!\n");
866 		spdk_app_stop(-1);
867 		return;
868 	}
869 
870 	spdk_bs_load(bs_dev, cli_context->next_func, cli_context);
871 }
872 
873 /*
874  * Lists all the blobs on this blobstore.
875  */
876 static void
877 list_bdevs(void)
878 {
879 	struct spdk_bdev *bdev = NULL;
880 
881 	printf("\nList bdevs:\n");
882 
883 	bdev = spdk_bdev_first();
884 	if (bdev == NULL) {
885 		printf("Could not find a bdev\n");
886 	}
887 	while (bdev) {
888 		printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev));
889 		printf("\tbdev Product Name: %s\n",
890 		       spdk_bdev_get_product_name(bdev));
891 		bdev = spdk_bdev_next(bdev);
892 	}
893 
894 	printf("\n");
895 	spdk_app_stop(0);
896 }
897 
898 /*
899  * Callback function for initializing a blob.
900  */
901 static void
902 bs_init_complete(void *cb_arg, struct spdk_blob_store *bs,
903 		 int bserrno)
904 {
905 	struct cli_context_t *cli_context = cb_arg;
906 
907 	if (bserrno) {
908 		unload_bs(cli_context, "Error in bs init callback",
909 			  bserrno);
910 		return;
911 	}
912 	cli_context->bs = bs;
913 	printf("blobstore init'd: (%p)\n", cli_context->bs);
914 
915 	unload_bs(cli_context, "", 0);
916 }
917 
918 /*
919  * Initialize a new blobstore.
920  */
921 static void
922 init_bs(struct cli_context_t *cli_context)
923 {
924 	struct spdk_bdev *bdev = NULL;
925 	struct spdk_bs_dev *bs_dev = NULL;
926 
927 	bdev = spdk_bdev_get_by_name(cli_context->bdev_name);
928 	if (bdev == NULL) {
929 		printf("Could not find a bdev\n");
930 		spdk_app_stop(-1);
931 		return;
932 	}
933 	printf("Blobstore using bdev Product Name: %s\n",
934 	       spdk_bdev_get_product_name(bdev));
935 
936 	bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL);
937 	if (bs_dev == NULL) {
938 		printf("Could not create blob bdev!!\n");
939 		spdk_app_stop(-1);
940 		return;
941 	}
942 
943 	spdk_bs_init(bs_dev, NULL, bs_init_complete,
944 		     cli_context);
945 }
946 
947 /*
948  * This is the function we pass into the SPDK framework that gets
949  * called first.
950  */
951 static void
952 cli_start(void *arg1, void *arg2)
953 {
954 	struct cli_context_t *cli_context = arg1;
955 
956 	printf("\n");
957 
958 	/*
959 	 * Decide what to do next based on cmd line parsing that
960 	 * happened earlier, in many cases we setup a function pointer
961 	 * to be used as a callback following a generic action like
962 	 * loading the blobstore.
963 	 */
964 	switch (cli_context->action) {
965 	case CLI_SET_SUPER:
966 		cli_context->next_func = &set_super_load_cb;
967 		load_bs(cli_context);
968 		break;
969 	case CLI_SHOW_BS:
970 		cli_context->next_func = &get_super_load_cb;
971 		load_bs(cli_context);
972 		break;
973 	case CLI_CREATE_BLOB:
974 		cli_context->next_func = &create_load_cb;
975 		load_bs(cli_context);
976 		break;
977 	case CLI_SET_XATTR:
978 	case CLI_REM_XATTR:
979 		cli_context->next_func = &xattr_load_cb;
980 		load_bs(cli_context);
981 		break;
982 	case CLI_SHOW_BLOB:
983 	case CLI_LIST_BLOBS:
984 		cli_context->next_func = &list_load_cb;
985 		load_bs(cli_context);
986 		break;
987 	case CLI_DUMP:
988 	case CLI_IMPORT:
989 		cli_context->next_func = &dmpimp_load_cb;
990 		load_bs(cli_context);
991 		break;
992 	case CLI_FILL:
993 		cli_context->next_func = &fill_load_cb;
994 		load_bs(cli_context);
995 		break;
996 	case CLI_INIT_BS:
997 		init_bs(cli_context);
998 		break;
999 	case CLI_LIST_BDEVS:
1000 		list_bdevs();
1001 		break;
1002 	default:
1003 		/* should never get here */
1004 		spdk_app_stop(-1);
1005 		break;
1006 	}
1007 }
1008 
1009 int
1010 main(int argc, char **argv)
1011 {
1012 	struct spdk_app_opts opts = {};
1013 	struct cli_context_t *cli_context = NULL;
1014 	const char *config_file = NULL;
1015 	int rc = 0;
1016 	int op;
1017 	bool cmd_chosen = false;
1018 
1019 	if (argc < 2) {
1020 		usage("ERROR: Invalid option\n");
1021 		exit(1);
1022 	}
1023 
1024 	cli_context = calloc(1, sizeof(struct cli_context_t));
1025 	if (cli_context == NULL) {
1026 		printf("ERROR: could not allocate context structure\n");
1027 		exit(-1);
1028 	}
1029 	cli_context->bdev_name = bdev_name;
1030 
1031 	while ((op = getopt(argc, argv, "c:d:f:il:m:n:p:r:s:x:")) != -1) {
1032 		switch (op) {
1033 		case 'c':
1034 			config_file = optarg;
1035 			break;
1036 		case 'd':
1037 			cli_context->action = CLI_DUMP;
1038 			cli_context->blobid = atoll(optarg);
1039 			break;
1040 		case 'f':
1041 			cli_context->action = CLI_FILL;
1042 			cli_context->blobid = atoll(optarg);
1043 			break;
1044 		case 'i':
1045 			cli_context->action = CLI_INIT_BS;
1046 			break;
1047 		case 'r':
1048 			cli_context->action = CLI_REM_XATTR;
1049 			cli_context->blobid = atoll(optarg);
1050 			break;
1051 		case 'l':
1052 			if (strcmp("bdevs", optarg) == 0) {
1053 				cli_context->action = CLI_LIST_BDEVS;
1054 			} else if (strcmp("blobs", optarg) == 0) {
1055 				cli_context->action = CLI_LIST_BLOBS;
1056 			} else {
1057 				usage("ERROR: invalid option for list\n");
1058 				cli_cleanup(cli_context);
1059 				exit(-1);
1060 			}
1061 			break;
1062 		case 'm':
1063 			cli_context->action = CLI_IMPORT;
1064 			cli_context->blobid = atoll(optarg);
1065 			break;
1066 		case 'n':
1067 			cli_context->num_clusters = atoi(optarg);
1068 			if (cli_context->num_clusters > 0) {
1069 				cli_context->action = CLI_CREATE_BLOB;
1070 			} else {
1071 				usage("ERROR: invalid option for new\n");
1072 				cli_cleanup(cli_context);
1073 				exit(-1);
1074 			}
1075 			break;
1076 		case 'p':
1077 			cli_context->action = CLI_SET_SUPER;
1078 			cli_context->superid = atoll(optarg);
1079 			break;
1080 		case 's':
1081 			if (strcmp("bs", optarg) == 0) {
1082 				cli_context->action = CLI_SHOW_BS;
1083 			} else {
1084 				cli_context->action = CLI_SHOW_BLOB;
1085 				cli_context->blobid = atoll(optarg);
1086 			}
1087 			break;
1088 		case 'x':
1089 			cli_context->action = CLI_SET_XATTR;
1090 			cli_context->blobid = atoll(optarg);
1091 			break;
1092 		default:
1093 			usage("ERROR: Invalid option\n");
1094 			cli_cleanup(cli_context);
1095 			exit(1);
1096 		}
1097 		/* config file is the only option that can be combined */
1098 		if (op != 'c') {
1099 			if (cmd_chosen) {
1100 				usage("Error: Please choose only one command\n");
1101 				cli_cleanup(cli_context);
1102 				exit(1);
1103 			} else {
1104 				cmd_chosen = true;
1105 			}
1106 		}
1107 	}
1108 
1109 	if (cmd_chosen == false) {
1110 		usage("Error: Please choose a command.\n");
1111 		exit(1);
1112 	}
1113 
1114 	/* if they don't supply a conf name, use the default */
1115 	if (!config_file) {
1116 		config_file = program_conf;
1117 	}
1118 
1119 	/* if the config file doesn't exist, tell them how to make one */
1120 	if (access(config_file, F_OK) == -1) {
1121 		printf("Error: No config file found.\n");
1122 		printf("To create a config file named 'blobcli.conf' for your NVMe device:\n");
1123 		printf("   <path to spdk>/scripts/gen_nvme.sh > blobcli.conf\n");
1124 		printf("and then re-run the cli tool.\n");
1125 		exit(1);
1126 	}
1127 
1128 	/* a few options require some extra paramters */
1129 	if (cli_context->action == CLI_SET_XATTR ||
1130 	    cli_context->action == CLI_REM_XATTR) {
1131 		snprintf(cli_context->key, BUFSIZE, "%s", argv[3]);
1132 		snprintf(cli_context->value, BUFSIZE, "%s", argv[4]);
1133 	}
1134 
1135 	if (cli_context->action == CLI_DUMP ||
1136 	    cli_context->action == CLI_IMPORT) {
1137 		snprintf(cli_context->file, BUFSIZE, "%s", argv[3]);
1138 	}
1139 
1140 	if (cli_context->action == CLI_FILL) {
1141 		cli_context->fill_value = atoi(argv[3]);
1142 	}
1143 
1144 	/* Set default values in opts struct along with name and conf file. */
1145 	spdk_app_opts_init(&opts);
1146 	opts.name = "blobcli";
1147 	opts.config_file = config_file;
1148 
1149 	/*
1150 	 * spdk_app_start() will block running cli_start() until
1151 	 * spdk_app_stop() is called by someone (not simply when
1152 	 * cli_start() returns)
1153 	 */
1154 	rc = spdk_app_start(&opts, cli_start, cli_context, NULL);
1155 	if (rc) {
1156 		printf("ERROR!\n");
1157 	}
1158 
1159 	/* Free up memory that we allocated */
1160 	cli_cleanup(cli_context);
1161 
1162 	/* Gracefully close out all of the SPDK subsystems. */
1163 	spdk_app_fini();
1164 	return rc;
1165 }
1166