4 * Copyright (c) Intel Corporation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
34 #include "spdk/stdinc.h"
36 #include "spdk/bdev.h"
38 #include "spdk/event.h"
39 #include "spdk/blob_bdev.h"
40 #include "spdk/blob.h"
42 #include "spdk/version.h"
43 #include "spdk/string.h"
44 #include "spdk/uuid.h"
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
51 #include "../lib/blob/blobstore.h"
52 static void cli_start(void *arg1
, void *arg2
);
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";
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
70 enum cli_action_type
{
92 #define STARTING_IO_UNIT 0
93 #define NUM_IO_UNITS 1
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.
100 struct cli_context_t
{
101 struct spdk_blob_store
*bs
;
102 struct spdk_blob
*blob
;
103 struct spdk_bs_dev
*bs_dev
;
105 spdk_blob_id superid
;
106 struct spdk_io_channel
*channel
;
109 uint64_t io_unit_size
;
110 uint64_t io_unit_count
;
111 uint64_t blob_io_units
;
112 uint64_t bytes_so_far
;
114 enum cli_action_type action
;
115 char key
[BUFSIZE
+ 1];
116 char value
[BUFSIZE
+ 1];
117 char file
[BUFSIZE
+ 1];
120 char bdev_name
[BUFSIZE
];
123 enum cli_mode_type cli_mode
;
124 const char *config_file
;
126 char *argv
[MAX_ARGS
];
128 char script_file
[BUFSIZE
+ 1];
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
];
140 char *cmdline
[MAX_SCRIPT_LINES
];
142 struct cli_script_t g_script
;
145 * Common printing of commands for CLI and shell modes.
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");
171 * Prints usage and relevant error message.
174 usage(struct cli_context_t
*cli_context
, char *msg
)
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",
185 printf("on the underlying device specified in the conf file passed\n");
186 printf("in as a command line option.\n");
188 if (!cli_context
|| cli_context
->cli_mode
!= CLI_MODE_SCRIPT
) {
194 * Free up memory that we allocated.
197 cli_cleanup(struct cli_context_t
*cli_context
)
199 if (cli_context
->buff
) {
200 spdk_dma_free(cli_context
->buff
);
202 if (cli_context
->cli_mode
== CLI_MODE_SCRIPT
) {
205 for (i
= 0; i
<= g_script
.max_index
; i
++) {
206 free(g_script
.cmdline
[i
]);
213 * Callback routine for the blobstore unload.
216 unload_complete(void *cb_arg
, int bserrno
)
218 struct cli_context_t
*cli_context
= cb_arg
;
221 printf("Error %d unloading the bobstore\n", bserrno
);
222 cli_context
->rc
= bserrno
;
226 * Quit if we're in cmd mode or exiting shell mode, otherwise
227 * clear the action field and start the main function again.
229 if (cli_context
->cli_mode
== CLI_MODE_CMD
||
230 cli_context
->action
== CLI_SHELL_EXIT
) {
231 spdk_app_stop(cli_context
->rc
);
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
, NULL
);
241 * Unload the blobstore.
244 unload_bs(struct cli_context_t
*cli_context
, char *msg
, int bserrno
)
247 printf("%s (err %d)\n", msg
, bserrno
);
248 cli_context
->rc
= bserrno
;
251 if (cli_context
->bs
) {
252 if (cli_context
->channel
) {
253 spdk_bs_free_io_channel(cli_context
->channel
);
254 cli_context
->channel
= NULL
;
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
);
264 * Callback for closing a blob.
267 close_cb(void *arg1
, int bserrno
)
269 struct cli_context_t
*cli_context
= arg1
;
272 unload_bs(cli_context
, "Error in close callback",
276 unload_bs(cli_context
, "", 0);
280 * Callback function for sync'ing metadata.
283 sync_cb(void *arg1
, int bserrno
)
285 struct cli_context_t
*cli_context
= arg1
;
288 unload_bs(cli_context
, "Error in sync callback",
293 spdk_blob_close(cli_context
->blob
, close_cb
, cli_context
);
297 resize_cb(void *cb_arg
, int bserrno
)
299 struct cli_context_t
*cli_context
= cb_arg
;
303 unload_bs(cli_context
, "Error in blob resize",
308 total
= spdk_blob_get_num_clusters(cli_context
->blob
);
309 printf("blob now has USED clusters of %" PRIu64
"\n",
313 * Always a good idea to sync after MD changes or the changes
314 * may be lost if things aren't closed cleanly.
316 spdk_blob_sync_md(cli_context
->blob
, sync_cb
, cli_context
);
320 * Callback function for opening a blob after creating.
323 open_now_resize_cb(void *cb_arg
, struct spdk_blob
*blob
, int bserrno
)
325 struct cli_context_t
*cli_context
= cb_arg
;
328 unload_bs(cli_context
, "Error in open completion",
332 cli_context
->blob
= blob
;
334 spdk_blob_resize(cli_context
->blob
, cli_context
->num_clusters
,
335 resize_cb
, cli_context
);
339 * Callback function for creating a blob.
342 blob_create_cb(void *arg1
, spdk_blob_id blobid
, int bserrno
)
344 struct cli_context_t
*cli_context
= arg1
;
347 unload_bs(cli_context
, "Error in blob create callback",
352 cli_context
->blobid
= blobid
;
353 printf("New blob id %" PRIu64
"\n", cli_context
->blobid
);
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
;
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
);
366 * Callback for get_super where we'll continue on to show blobstore info.
369 show_bs_cb(void *arg1
, spdk_blob_id blobid
, int bserrno
)
371 struct cli_context_t
*cli_context
= arg1
;
372 struct spdk_bs_type bstype
;
374 struct spdk_bdev
*bdev
= NULL
;
376 if (bserrno
&& bserrno
!= -ENOENT
) {
377 unload_bs(cli_context
, "Error in get_super callback",
381 cli_context
->superid
= blobid
;
383 bdev
= spdk_bdev_get_by_name(cli_context
->bdev_name
);
385 unload_bs(cli_context
, "Error w/bdev in get_super callback",
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
);
395 if (bserrno
!= -ENOENT
) {
396 printf("\tsuper blob ID: %" PRIu64
"\n", cli_context
->superid
);
398 printf("\tsuper blob ID: none assigned\n");
401 printf("\tpage size: %" PRIu64
"\n", cli_context
->page_size
);
402 printf("\tio unit size: %" PRIu64
"\n", cli_context
->io_unit_size
);
404 val
= spdk_bs_get_cluster_size(cli_context
->bs
);
405 printf("\tcluster size: %" PRIu64
"\n", val
);
407 val
= spdk_bs_free_cluster_count(cli_context
->bs
);
408 printf("\t# free clusters: %" PRIu64
"\n", val
);
410 bstype
= spdk_bs_get_bstype(cli_context
->bs
);
411 spdk_trace_dump(stdout
, "\tblobstore type:", &bstype
, sizeof(bstype
));
414 * Private info isn't accessible via the public API but
415 * may be useful for debug of blobstore based applications.
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
);
423 unload_bs(cli_context
, "", 0);
427 * Show detailed info about a particular blob.
430 show_blob(struct cli_context_t
*cli_context
)
433 struct spdk_xattr_names
*names
;
439 printf("Blob Public Info:\n");
441 printf("blob ID: %" PRIu64
"\n", cli_context
->blobid
);
443 val
= spdk_blob_get_num_clusters(cli_context
->blob
);
444 printf("# of clusters: %" PRIu64
"\n", val
);
446 printf("# of bytes: %" PRIu64
"\n",
447 val
* spdk_bs_get_cluster_size(cli_context
->bs
));
449 val
= spdk_blob_get_num_pages(cli_context
->blob
);
450 printf("# of pages: %" PRIu64
"\n", val
);
452 spdk_blob_get_xattr_names(cli_context
->blob
, &names
);
454 printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names
));
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
),
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;
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_trace_dump(stdout
, "", value
, value_len
);
473 * Private info isn't accessible via the public API but
474 * may be useful for debug of blobstore based applications.
476 printf("\nBlob Private Info:\n");
477 switch (cli_context
->blob
->state
) {
478 case SPDK_BLOB_STATE_DIRTY
:
479 printf("state: DIRTY\n");
481 case SPDK_BLOB_STATE_CLEAN
:
482 printf("state: CLEAN\n");
484 case SPDK_BLOB_STATE_LOADING
:
485 printf("state: LOADING\n");
488 printf("state: UNKNOWN\n");
491 printf("open ref count: %d\n",
492 cli_context
->blob
->open_ref
);
494 spdk_xattr_names_free(names
);
498 * Callback for getting the first blob, shared with simple blob listing as well.
501 blob_iter_cb(void *arg1
, struct spdk_blob
*blob
, int bserrno
)
503 struct cli_context_t
*cli_context
= arg1
;
506 if (bserrno
== -ENOENT
) {
507 /* this simply means there are no more blobs */
508 unload_bs(cli_context
, "", 0);
510 unload_bs(cli_context
, "Error in blob iter callback",
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
) {
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.
527 cli_context
->blob
= blob
;
528 show_blob(cli_context
);
531 spdk_bs_iter_next(cli_context
->bs
, blob
, blob_iter_cb
, cli_context
);
535 * Callback for setting the super blob ID.
538 set_super_cb(void *arg1
, int bserrno
)
540 struct cli_context_t
*cli_context
= arg1
;
543 unload_bs(cli_context
, "Error in set_super callback",
548 printf("Super Blob ID has been set.\n");
549 unload_bs(cli_context
, "", 0);
553 * Callback for set_xattr_open where we set or delete xattrs.
556 set_xattr_cb(void *cb_arg
, struct spdk_blob
*blob
, int bserrno
)
558 struct cli_context_t
*cli_context
= cb_arg
;
561 unload_bs(cli_context
, "Error in blob open callback",
565 cli_context
->blob
= blob
;
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");
572 spdk_blob_remove_xattr(cli_context
->blob
, cli_context
->key
);
573 printf("Xattr has been removed.\n");
576 spdk_blob_sync_md(cli_context
->blob
, sync_cb
, cli_context
);
580 * Callback function for reading a blob for dumping to a file.
583 read_dump_cb(void *arg1
, int bserrno
)
585 struct cli_context_t
*cli_context
= arg1
;
586 uint64_t bytes_written
;
589 fclose(cli_context
->fp
);
590 unload_bs(cli_context
, "Error in read completion",
595 bytes_written
= fwrite(cli_context
->buff
, NUM_IO_UNITS
, cli_context
->io_unit_size
,
597 if (bytes_written
!= cli_context
->io_unit_size
) {
598 fclose(cli_context
->fp
);
599 unload_bs(cli_context
, "Error with fwrite",
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
);
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
);
619 * Callback for write completion on the import of a file to a blob.
622 write_imp_cb(void *arg1
, int bserrno
)
624 struct cli_context_t
*cli_context
= arg1
;
628 fclose(cli_context
->fp
);
629 unload_bs(cli_context
, "Error in write completion",
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
,
639 cli_context
->bytes_so_far
+= bytes_read
;
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
);
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
651 memset(cli_context
->buff
, 0, cli_context
->io_unit_size
);
653 if (++cli_context
->io_unit_count
< cli_context
->blob_io_units
) {
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
);
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
);
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.
673 dump_imp_open_cb(void *cb_arg
, struct spdk_blob
*blob
, int bserrno
)
675 struct cli_context_t
*cli_context
= cb_arg
;
678 unload_bs(cli_context
, "Error in blob open callback",
682 cli_context
->blob
= blob
;
685 * We'll transfer just one io_unit at a time to keep the buffer
686 * small. This could be bigger of course.
688 cli_context
->buff
= spdk_dma_malloc(cli_context
->io_unit_size
,
690 if (cli_context
->buff
== NULL
) {
691 printf("Error in allocating memory\n");
692 spdk_blob_close(cli_context
->blob
, close_cb
, cli_context
);
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
);
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
);
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
);
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
,
726 /* if the file is < a io_unit, fill the rest with 0s */
727 if (cli_context
->filesize
< cli_context
->io_unit_size
) {
729 cli_context
->buff
+ cli_context
->filesize
;
732 cli_context
->io_unit_size
- cli_context
->filesize
);
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
);
742 * Callback function for writing a specific pattern to io_unit 0.
745 write_cb(void *arg1
, int bserrno
)
747 struct cli_context_t
*cli_context
= arg1
;
750 unload_bs(cli_context
, "Error in write completion",
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
);
761 printf("\nBlob fill complete (with 0x%x).\n", cli_context
->fill_value
);
762 spdk_blob_close(cli_context
->blob
, close_cb
, cli_context
);
767 * Callback function to fill a blob with a value, callback from open.
770 fill_blob_cb(void *arg1
, struct spdk_blob
*blob
, int bserrno
)
772 struct cli_context_t
*cli_context
= arg1
;
775 unload_bs(cli_context
, "Error in open callback",
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_dma_malloc(cli_context
->io_unit_size
,
785 if (cli_context
->buff
== NULL
) {
786 unload_bs(cli_context
, "Error in allocating memory",
791 memset(cli_context
->buff
, cli_context
->fill_value
,
792 cli_context
->io_unit_size
);
794 spdk_blob_io_write(cli_context
->blob
, cli_context
->channel
,
796 STARTING_IO_UNIT
, NUM_IO_UNITS
, write_cb
, cli_context
);
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.
805 load_bs_cb(void *arg1
, struct spdk_blob_store
*bs
, int bserrno
)
807 struct cli_context_t
*cli_context
= arg1
;
810 unload_bs(cli_context
, "Error in load callback",
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",
825 switch (cli_context
->action
) {
827 spdk_bs_set_super(cli_context
->bs
, cli_context
->superid
,
828 set_super_cb
, cli_context
);
831 spdk_bs_get_super(cli_context
->bs
, show_bs_cb
, cli_context
);
833 case CLI_CREATE_BLOB
:
834 spdk_bs_create_blob(cli_context
->bs
, blob_create_cb
, cli_context
);
838 spdk_bs_open_blob(cli_context
->bs
, cli_context
->blobid
,
839 set_xattr_cb
, cli_context
);
843 spdk_bs_iter_first(cli_context
->bs
, blob_iter_cb
, cli_context
);
847 case CLI_IMPORT_BLOB
:
848 spdk_bs_open_blob(cli_context
->bs
, cli_context
->blobid
,
849 dump_imp_open_cb
, cli_context
);
852 spdk_bs_open_blob(cli_context
->bs
, cli_context
->blobid
,
853 fill_blob_cb
, cli_context
);
857 /* should never get here */
864 * Load the blobstore.
867 load_bs(struct cli_context_t
*cli_context
)
869 struct spdk_bdev
*bdev
= NULL
;
870 struct spdk_bs_dev
*bs_dev
= NULL
;
872 bdev
= spdk_bdev_get_by_name(cli_context
->bdev_name
);
874 printf("Could not find a bdev\n");
879 bs_dev
= spdk_bdev_create_bs_dev(bdev
, NULL
, NULL
);
880 if (bs_dev
== NULL
) {
881 printf("Could not create blob bdev!!\n");
886 spdk_bs_load(bs_dev
, NULL
, load_bs_cb
, cli_context
);
890 * Lists all the blobs on this blobstore.
893 list_bdevs(struct cli_context_t
*cli_context
)
895 struct spdk_bdev
*bdev
= NULL
;
897 printf("\nList bdevs:\n");
899 bdev
= spdk_bdev_first();
901 printf("Could not find a bdev\n");
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
);
911 if (cli_context
->cli_mode
== CLI_MODE_CMD
) {
914 cli_context
->action
= CLI_NONE
;
915 cli_start(cli_context
, NULL
);
920 * Callback function for initializing a blob.
923 bs_init_cb(void *cb_arg
, struct spdk_blob_store
*bs
,
926 struct cli_context_t
*cli_context
= cb_arg
;
929 unload_bs(cli_context
, "Error in bs init callback",
933 cli_context
->bs
= bs
;
934 printf("blobstore init'd: (%p)\n", cli_context
->bs
);
936 unload_bs(cli_context
, "", 0);
940 * Initialize a new blobstore.
943 init_bs(struct cli_context_t
*cli_context
)
945 struct spdk_bdev
*bdev
= NULL
;
947 bdev
= spdk_bdev_get_by_name(cli_context
->bdev_name
);
949 printf("Could not find a bdev\n");
953 printf("Init blobstore using bdev Product Name: %s\n",
954 spdk_bdev_get_product_name(bdev
));
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");
963 spdk_bs_init(cli_context
->bs_dev
, NULL
, bs_init_cb
,
968 spdk_bsdump_done(void *arg
, int bserrno
)
970 struct cli_context_t
*cli_context
= arg
;
972 if (cli_context
->cli_mode
== CLI_MODE_CMD
) {
975 cli_context
->action
= CLI_NONE
;
976 cli_start(cli_context
, NULL
);
981 bsdump_print_xattr(FILE *fp
, const char *bstype
, const char *name
, const void *value
,
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)) {
990 memcpy(&length
, value
, sizeof(length
));
991 fprintf(fp
, "%" PRIu64
, length
);
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
];
1001 spdk_uuid_fmt_lower(uuid
, sizeof(uuid
), (struct spdk_uuid
*)value
);
1002 fprintf(fp
, "%s", uuid
);
1012 * Dump metadata of an existing blobstore in a human-readable format.
1015 dump_bs(struct cli_context_t
*cli_context
)
1017 struct spdk_bdev
*bdev
= NULL
;
1019 bdev
= spdk_bdev_get_by_name(cli_context
->bdev_name
);
1021 printf("Could not find a bdev\n");
1025 printf("Init blobstore using bdev Product Name: %s\n",
1026 spdk_bdev_get_product_name(bdev
));
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");
1035 spdk_bs_dump(cli_context
->bs_dev
, stdout
, bsdump_print_xattr
, spdk_bsdump_done
, cli_context
);
1039 * Common cmd/option parser for command and shell modes.
1042 cmd_parser(int argc
, char **argv
, struct cli_context_t
*cli_context
)
1048 while ((op
= getopt(argc
, argv
, "b:c:d:f:hil:m:n:p:r:s:DST:Xx:")) != -1) {
1051 if (strcmp(cli_context
->bdev_name
, "") == 0) {
1052 snprintf(cli_context
->bdev_name
, BUFSIZE
, "%s", optarg
);
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");
1059 if (cli_context
->app_started
== false) {
1060 cli_context
->config_file
= optarg
;
1062 usage(cli_context
, "ERROR: -c option not valid during shell mode.\n");
1067 cli_context
->action
= CLI_DUMP_BS
;
1070 if (argv
[optind
] != NULL
) {
1072 cli_context
->action
= CLI_DUMP_BLOB
;
1073 cli_context
->blobid
= atoll(optarg
);
1074 snprintf(cli_context
->file
, BUFSIZE
, "%s", argv
[optind
]);
1076 usage(cli_context
, "ERROR: missing parameter.\n");
1080 if (argv
[optind
] != NULL
) {
1082 cli_context
->action
= CLI_FILL
;
1083 cli_context
->blobid
= atoll(optarg
);
1084 cli_context
->fill_value
= atoi(argv
[optind
]);
1086 usage(cli_context
, "ERROR: missing parameter.\n");
1091 cli_context
->action
= CLI_HELP
;
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') {
1099 cli_context
->action
= CLI_INIT_BS
;
1101 if (cli_context
->cli_mode
== CLI_MODE_CMD
) {
1109 cli_context
->action
= CLI_INIT_BS
;
1113 if (argv
[optind
] != NULL
) {
1115 cli_context
->action
= CLI_REM_XATTR
;
1116 cli_context
->blobid
= atoll(optarg
);
1117 snprintf(cli_context
->key
, BUFSIZE
, "%s", argv
[optind
]);
1119 usage(cli_context
, "ERROR: missing parameter.\n");
1123 if (strcmp("bdevs", optarg
) == 0) {
1125 cli_context
->action
= CLI_LIST_BDEVS
;
1126 } else if (strcmp("blobs", optarg
) == 0) {
1128 cli_context
->action
= CLI_LIST_BLOBS
;
1130 usage(cli_context
, "ERROR: invalid option for list\n");
1134 if (argv
[optind
] != NULL
) {
1136 cli_context
->action
= CLI_IMPORT_BLOB
;
1137 cli_context
->blobid
= atoll(optarg
);
1138 snprintf(cli_context
->file
, BUFSIZE
, "%s", argv
[optind
]);
1140 usage(cli_context
, "ERROR: missing parameter.\n");
1144 cli_context
->num_clusters
= atoi(optarg
);
1145 if (cli_context
->num_clusters
> 0) {
1147 cli_context
->action
= CLI_CREATE_BLOB
;
1149 usage(cli_context
, "ERROR: invalid option for new\n");
1154 cli_context
->action
= CLI_SET_SUPER
;
1155 cli_context
->superid
= atoll(optarg
);
1158 if (cli_context
->cli_mode
== CLI_MODE_CMD
) {
1160 cli_context
->cli_mode
= CLI_MODE_SHELL
;
1162 cli_context
->action
= CLI_NONE
;
1166 if (strcmp("bs", optarg
) == 0) {
1167 cli_context
->action
= CLI_SHOW_BS
;
1169 cli_context
->action
= CLI_SHOW_BLOB
;
1170 cli_context
->blobid
= atoll(optarg
);
1174 if (cli_context
->cli_mode
== CLI_MODE_CMD
) {
1176 cli_context
->cli_mode
= CLI_MODE_SCRIPT
;
1177 if (argv
[optind
] && (strcmp("ignore", argv
[optind
]) == 0)) {
1178 g_script
.ignore_errors
= true;
1180 g_script
.ignore_errors
= false;
1182 snprintf(cli_context
->script_file
, BUFSIZE
, "%s", optarg
);
1184 cli_context
->action
= CLI_NONE
;
1189 cli_context
->action
= CLI_SHELL_EXIT
;
1192 if (argv
[optind
] != NULL
|| argv
[optind
+ 1] != NULL
) {
1194 cli_context
->action
= CLI_SET_XATTR
;
1195 cli_context
->blobid
= atoll(optarg
);
1196 snprintf(cli_context
->key
, BUFSIZE
, "%s", argv
[optind
]);
1197 snprintf(cli_context
->value
, BUFSIZE
, "%s", argv
[optind
+ 1]);
1199 usage(cli_context
, "ERROR: missing parameter.\n");
1203 usage(cli_context
, "ERROR: invalid option\n");
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");
1211 if (cli_context
->cli_mode
== CLI_MODE_CMD
&& cmd_chosen
== 0) {
1212 usage(cli_context
, "Error: Please choose a command.\n");
1216 * We don't check the local boolean because in some modes it will have been set
1217 * on and earlier command.
1219 if (strcmp(cli_context
->bdev_name
, "") == 0) {
1220 usage(cli_context
, "Error: -b option is required.\n");
1224 /* in shell mode we'll call getopt multiple times so need to reset its index */
1226 return (cmd_chosen
== 1);
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.
1235 line_parser(struct cli_context_t
*cli_context
)
1240 int start_idx
= cli_context
->argc
;
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
) {
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.
1251 cli_context
->argv
[cli_context
->argc
] = strdup(tok
);
1252 if (tok
[0] == '$' && tok
[1] == 'B') {
1254 blob_num
= atoi(tok
);
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");
1262 if (g_script
.blobid
[blob_num
] == 0) {
1263 printf("ERROR: There is no blob for $B%d\n",
1266 snprintf(cli_context
->argv
[cli_context
->argc
], BUFSIZE
,
1267 "%" PRIu64
, g_script
.blobid
[blob_num
]);
1269 printf("ERROR: Invalid token or exceeded max blobs of %d\n",
1273 cli_context
->argc
++;
1274 tok
= strtok(NULL
, " ");
1277 /* call parse cmd line with user input as args */
1278 cmd_chosen
= cmd_parser(cli_context
->argc
, &cli_context
->argv
[0], cli_context
);
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
;
1285 cli_context
->argc
= 1;
1287 g_script
.cmdline_idx
++;
1288 assert(g_script
.cmdline_idx
< MAX_SCRIPT_LINES
);
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
;
1297 unload_bs(cli_context
, "", 0);
1299 printf("** Skipping **\n");
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.
1313 parse_script(struct cli_context_t
*cli_context
)
1316 size_t bufsize
= BUFSIZE
;
1317 int64_t bytes_in
= 0;
1320 /* initialize global script values */
1321 for (i
= 0; i
< MAX_SCRIPT_BLOBS
; i
++) {
1322 g_script
.blobid
[i
] = 0;
1324 g_script
.blobid_idx
= 0;
1325 g_script
.cmdline_idx
= 0;
1328 fp
= fopen(cli_context
->script_file
, "r");
1330 printf("ERROR: unable to open script: %s\n",
1331 cli_context
->script_file
);
1332 cli_cleanup(cli_context
);
1337 bytes_in
= getline(&g_script
.cmdline
[i
], &bufsize
, fp
);
1339 /* replace newline with null */
1340 spdk_str_chomp(g_script
.cmdline
[i
]);
1342 /* ignore comments */
1343 if (g_script
.cmdline
[i
][0] != '#') {
1347 } while (bytes_in
!= -1 && i
< MAX_SCRIPT_LINES
- 1);
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
) {
1355 for (j
= 0; j
< i
; j
++) {
1356 free(g_script
.cmdline
[j
]);
1357 g_script
.cmdline
[j
] = NULL
;
1359 unload_bs(cli_context
, "ERROR: unable to alloc memory.\n", 0);
1361 snprintf(g_script
.cmdline
[i
], BUFSIZE
, "%s", "-X");
1362 g_script
.max_index
= i
;
1366 * Provides for a shell interface as opposed to one shot command line.
1369 cli_shell(void *arg1
, void *arg2
)
1371 struct cli_context_t
*cli_context
= arg1
;
1373 ssize_t buf_size
= 0;
1374 ssize_t bytes_in
= 0;
1375 ssize_t tok_len
= 0;
1377 bool cmd_chosen
= false;
1378 int start_idx
= cli_context
->argc
;
1382 bytes_in
= getline(&line
, &buf_size
, stdin
);
1384 /* If getline() failed (EOF), exit the shell. */
1387 cli_context
->action
= CLI_SHELL_EXIT
;
1391 /* parse input and update cli_context so we can use common option parser */
1393 tok
= strtok(line
, " ");
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
, " ");
1402 /* replace newline on last arg with null */
1404 spdk_str_chomp(cli_context
->argv
[cli_context
->argc
- 1]);
1407 /* call parse cmd line with user input as args */
1408 cmd_chosen
= cmd_parser(cli_context
->argc
, &cli_context
->argv
[0], cli_context
);
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
;
1415 cli_context
->argc
= 1;
1423 * This is the function we pass into the SPDK framework that gets
1427 cli_start(void *arg1
, void *arg2
)
1429 struct cli_context_t
*cli_context
= arg1
;
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.
1435 if (cli_context
->cli_mode
== CLI_MODE_SCRIPT
) {
1436 while (line_parser(cli_context
) == false);
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.
1444 if (cli_context
->action
== CLI_NONE
) {
1445 while (cli_shell(cli_context
, NULL
) == false);
1448 /* Decide what to do next based on cmd line parsing. */
1449 switch (cli_context
->action
) {
1452 case CLI_CREATE_BLOB
:
1456 case CLI_LIST_BLOBS
:
1458 case CLI_IMPORT_BLOB
:
1460 load_bs(cli_context
);
1463 init_bs(cli_context
);
1466 dump_bs(cli_context
);
1468 case CLI_LIST_BDEVS
:
1469 list_bdevs(cli_context
);
1471 case CLI_SHELL_EXIT
:
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.
1481 usage(cli_context
, "");
1482 unload_complete(cli_context
, 0);
1485 /* should never get here */
1492 main(int argc
, char **argv
)
1494 struct spdk_app_opts opts
= {};
1495 struct cli_context_t
*cli_context
= NULL
;
1500 usage(cli_context
, "ERROR: Invalid option\n");
1504 cli_context
= calloc(1, sizeof(struct cli_context_t
));
1505 if (cli_context
== NULL
) {
1506 printf("ERROR: could not allocate context structure\n");
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;
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
);
1524 /* after displaying help, just exit */
1525 if (cli_context
->action
== CLI_HELP
) {
1526 usage(cli_context
, "");
1527 cli_cleanup(cli_context
);
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
;
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");
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.
1549 if (cli_context
->cli_mode
== CLI_MODE_SCRIPT
) {
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.
1555 parse_script(cli_context
);
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
;
1563 cli_context
->app_started
= true;
1564 rc
= spdk_app_start(&opts
, cli_start
, cli_context
, NULL
);
1569 /* Free up memory that we allocated */
1570 cli_cleanup(cli_context
);
1572 /* Gracefully close out all of the SPDK subsystems. */