1488570ebSJim Harris /* SPDX-License-Identifier: BSD-3-Clause 2a6dbe372Spaul luse * Copyright (C) 2017 Intel Corporation. 38fbbfccdSPaul Luse * All rights reserved. 4ab2aad85SMike Gerdts * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. 58fbbfccdSPaul Luse */ 68fbbfccdSPaul Luse 78fbbfccdSPaul Luse #include "spdk/stdinc.h" 88fbbfccdSPaul Luse 98fbbfccdSPaul Luse #include "spdk/bdev.h" 108fbbfccdSPaul Luse #include "spdk/env.h" 118fbbfccdSPaul Luse #include "spdk/event.h" 128fbbfccdSPaul Luse #include "spdk/blob_bdev.h" 138fbbfccdSPaul Luse #include "spdk/blob.h" 148fbbfccdSPaul Luse #include "spdk/log.h" 158fbbfccdSPaul Luse #include "spdk/version.h" 16c5e63c71SPaul Luse #include "spdk/string.h" 17d1d22046SJim Harris #include "spdk/uuid.h" 188fbbfccdSPaul Luse 198fbbfccdSPaul Luse /* 20866f15b3SPaul Luse * The following is not a public header file, but the CLI does expose 21b7876f9aSJosh Soref * some internals of blobstore for dev/debug purposes so we 228fbbfccdSPaul Luse * include it here. 238fbbfccdSPaul Luse */ 248fbbfccdSPaul Luse #include "../lib/blob/blobstore.h" 25deb8ee5cSBen Walker static void cli_start(void *arg1); 268fbbfccdSPaul Luse 278fbbfccdSPaul Luse static const char *program_name = "blobcli"; 286a41e84cSWANGHAILIANG /* default name for .json file, any name can be used however with -j switch */ 296a41e84cSWANGHAILIANG static const char *program_conf = "blobcli.json"; 308fbbfccdSPaul Luse 31c5e63c71SPaul Luse /* 32c5e63c71SPaul Luse * CMD mode runs one command at a time which can be annoying as the init takes 33c5e63c71SPaul Luse * a few seconds, so the shell mode, invoked with -S, does the init once and gives 34e5044f79SPaul Luse * the user an interactive shell instead. With script mode init is also done just 35e5044f79SPaul Luse * once. 36c5e63c71SPaul Luse */ 37c5e63c71SPaul Luse enum cli_mode_type { 38c5e63c71SPaul Luse CLI_MODE_CMD, 39e5044f79SPaul Luse CLI_MODE_SHELL, 40e5044f79SPaul Luse CLI_MODE_SCRIPT 41c5e63c71SPaul Luse }; 42c5e63c71SPaul Luse 438fbbfccdSPaul Luse enum cli_action_type { 44c5e63c71SPaul Luse CLI_NONE, 454952f254SDaniel Verkamp CLI_IMPORT_BLOB, 464952f254SDaniel Verkamp CLI_DUMP_BLOB, 478fbbfccdSPaul Luse CLI_FILL, 488fbbfccdSPaul Luse CLI_REM_XATTR, 498fbbfccdSPaul Luse CLI_SET_XATTR, 508fbbfccdSPaul Luse CLI_SET_SUPER, 518fbbfccdSPaul Luse CLI_SHOW_BS, 528fbbfccdSPaul Luse CLI_SHOW_BLOB, 538fbbfccdSPaul Luse CLI_CREATE_BLOB, 548fbbfccdSPaul Luse CLI_LIST_BDEVS, 558fbbfccdSPaul Luse CLI_LIST_BLOBS, 56c5e63c71SPaul Luse CLI_INIT_BS, 57d1d22046SJim Harris CLI_DUMP_BS, 58c5e63c71SPaul Luse CLI_SHELL_EXIT, 59e5044f79SPaul Luse CLI_HELP, 60ff4fd58bSMike Gerdts CLI_RECOVER, 613453391bSMike Gerdts CLI_DELETE_BLOB, 628fbbfccdSPaul Luse }; 638fbbfccdSPaul Luse 64866f15b3SPaul Luse #define BUFSIZE 255 65e5044f79SPaul Luse #define MAX_ARGS 16 66866f15b3SPaul Luse #define ALIGN_4K 4096 676751b44bSTomasz Zawadzki #define STARTING_IO_UNIT 0 686751b44bSTomasz Zawadzki #define NUM_IO_UNITS 1 69e5044f79SPaul Luse 70c5e63c71SPaul Luse /* 71c5e63c71SPaul Luse * The CLI uses the SPDK app framework so is async and callback driven. A 72c5e63c71SPaul Luse * pointer to this structure is passed to SPDK calls and returned in the 73c5e63c71SPaul Luse * callbacks for easy access to all the info we may need. 74c5e63c71SPaul Luse */ 758fbbfccdSPaul Luse struct cli_context_t { 768fbbfccdSPaul Luse struct spdk_blob_store *bs; 778fbbfccdSPaul Luse struct spdk_blob *blob; 78c5e63c71SPaul Luse struct spdk_bs_dev *bs_dev; 798fbbfccdSPaul Luse spdk_blob_id blobid; 808fbbfccdSPaul Luse spdk_blob_id superid; 818fbbfccdSPaul Luse struct spdk_io_channel *channel; 828fbbfccdSPaul Luse uint8_t *buff; 838fbbfccdSPaul Luse uint64_t page_size; 846751b44bSTomasz Zawadzki uint64_t io_unit_size; 856751b44bSTomasz Zawadzki uint64_t io_unit_count; 866751b44bSTomasz Zawadzki uint64_t blob_io_units; 878fbbfccdSPaul Luse uint64_t bytes_so_far; 888fbbfccdSPaul Luse FILE *fp; 898fbbfccdSPaul Luse enum cli_action_type action; 908fbbfccdSPaul Luse char key[BUFSIZE + 1]; 918fbbfccdSPaul Luse char value[BUFSIZE + 1]; 928fbbfccdSPaul Luse char file[BUFSIZE + 1]; 938fbbfccdSPaul Luse uint64_t filesize; 948fbbfccdSPaul Luse int fill_value; 95cfa4cae0SPaul Luse char bdev_name[BUFSIZE]; 968fbbfccdSPaul Luse int rc; 978fbbfccdSPaul Luse int num_clusters; 98c5e63c71SPaul Luse enum cli_mode_type cli_mode; 99c5e63c71SPaul Luse const char *config_file; 100c5e63c71SPaul Luse int argc; 101c5e63c71SPaul Luse char *argv[MAX_ARGS]; 102c5e63c71SPaul Luse bool app_started; 103e5044f79SPaul Luse char script_file[BUFSIZE + 1]; 1048fbbfccdSPaul Luse }; 1058fbbfccdSPaul Luse 106e5044f79SPaul Luse /* we store a bunch of stuff in a global struct for use by scripting mode */ 107e5044f79SPaul Luse #define MAX_SCRIPT_LINES 64 108e5044f79SPaul Luse #define MAX_SCRIPT_BLOBS 16 109e5044f79SPaul Luse struct cli_script_t { 110e5044f79SPaul Luse spdk_blob_id blobid[MAX_SCRIPT_BLOBS]; 111e5044f79SPaul Luse int blobid_idx; 112e5044f79SPaul Luse int max_index; 113e5044f79SPaul Luse int cmdline_idx; 114e5044f79SPaul Luse bool ignore_errors; 115e5044f79SPaul Luse char *cmdline[MAX_SCRIPT_LINES]; 116e5044f79SPaul Luse }; 117e5044f79SPaul Luse struct cli_script_t g_script; 118e5044f79SPaul Luse 1198fbbfccdSPaul Luse /* 120c5e63c71SPaul Luse * Common printing of commands for CLI and shell modes. 121c5e63c71SPaul Luse */ 122c5e63c71SPaul Luse static void 123e5044f79SPaul Luse print_cmds(void) 124c5e63c71SPaul Luse { 125c5e63c71SPaul Luse printf("\nCommands include:\n"); 126cfa4cae0SPaul Luse printf("\t-b bdev - name of the block device to use (example: Nvme0n1)\n"); 127c5e63c71SPaul Luse printf("\t-d <blobid> filename - dump contents of a blob to a file\n"); 128d1d22046SJim Harris printf("\t-D - dump metadata contents of an existing blobstore\n"); 129c5e63c71SPaul Luse printf("\t-f <blobid> value - fill a blob with a decimal value\n"); 130c5e63c71SPaul Luse printf("\t-h - this help screen\n"); 131c5e63c71SPaul Luse printf("\t-i - initialize a blobstore\n"); 132c5e63c71SPaul Luse printf("\t-l bdevs | blobs - list either available bdevs or existing blobs\n"); 133c5e63c71SPaul Luse printf("\t-m <blobid> filename - import contents of a file to a blob\n"); 134c5e63c71SPaul Luse printf("\t-n <# clusters> - create new blob\n"); 135c5e63c71SPaul Luse printf("\t-p <blobid> - set the superblob to the ID provided\n"); 136c5e63c71SPaul Luse printf("\t-r <blobid> name - remove xattr name/value pair\n"); 137ff4fd58bSMike Gerdts printf("\t-R - recover the blobstore: like fsck for the blobstore\n"); 138c5e63c71SPaul Luse printf("\t-s <blobid> | bs - show blob info or blobstore info\n"); 139c5e63c71SPaul Luse printf("\t-S - enter interactive shell mode\n"); 140e5044f79SPaul Luse printf("\t-T <filename> - automated script mode\n"); 1413453391bSMike Gerdts printf("\t-w <blobid> - delete (whack) a blob\n"); 142d14c3bfbSMike Gerdts printf("\t-x <blobid> name value - set xattr name/value pair\n"); 143d14c3bfbSMike Gerdts printf("\t-X - exit when in interactive shell mode\n"); 144c5e63c71SPaul Luse printf("\n"); 145c5e63c71SPaul Luse } 146c5e63c71SPaul Luse 147c5e63c71SPaul Luse /* 1488fbbfccdSPaul Luse * Prints usage and relevant error message. 1498fbbfccdSPaul Luse */ 1508fbbfccdSPaul Luse static void 151e5044f79SPaul Luse usage(struct cli_context_t *cli_context, char *msg) 1528fbbfccdSPaul Luse { 1538fbbfccdSPaul Luse if (msg) { 1548fbbfccdSPaul Luse printf("%s", msg); 1558fbbfccdSPaul Luse } 156e5044f79SPaul Luse 1577fc14171SDaniel Verkamp if (!cli_context || cli_context->cli_mode == CLI_MODE_CMD) { 1588fbbfccdSPaul Luse printf("Version %s\n", SPDK_VERSION_STRING); 1596a41e84cSWANGHAILIANG printf("Usage: %s [-j SPDK josn_config_file] Command\n", program_name); 1608fbbfccdSPaul Luse printf("\n%s is a command line tool for interacting with blobstore\n", 1618fbbfccdSPaul Luse program_name); 1628fbbfccdSPaul Luse printf("on the underlying device specified in the conf file passed\n"); 1638fbbfccdSPaul Luse printf("in as a command line option.\n"); 164e5044f79SPaul Luse } 1657fc14171SDaniel Verkamp if (!cli_context || cli_context->cli_mode != CLI_MODE_SCRIPT) { 166e5044f79SPaul Luse print_cmds(); 167e5044f79SPaul Luse } 1688fbbfccdSPaul Luse } 1698fbbfccdSPaul Luse 1708fbbfccdSPaul Luse /* 1718fbbfccdSPaul Luse * Free up memory that we allocated. 1728fbbfccdSPaul Luse */ 1738fbbfccdSPaul Luse static void 1748fbbfccdSPaul Luse cli_cleanup(struct cli_context_t *cli_context) 1758fbbfccdSPaul Luse { 1768fbbfccdSPaul Luse if (cli_context->buff) { 17727a23a33SDarek Stojaczyk spdk_free(cli_context->buff); 1788fbbfccdSPaul Luse } 179e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 180e5044f79SPaul Luse int i; 181e5044f79SPaul Luse 182e5044f79SPaul Luse for (i = 0; i <= g_script.max_index; i++) { 183e5044f79SPaul Luse free(g_script.cmdline[i]); 184e5044f79SPaul Luse } 185e5044f79SPaul Luse } 1868fbbfccdSPaul Luse free(cli_context); 1878fbbfccdSPaul Luse } 1888fbbfccdSPaul Luse 1898fbbfccdSPaul Luse /* 1908fbbfccdSPaul Luse * Callback routine for the blobstore unload. 1918fbbfccdSPaul Luse */ 1928fbbfccdSPaul Luse static void 1938fbbfccdSPaul Luse unload_complete(void *cb_arg, int bserrno) 1948fbbfccdSPaul Luse { 1958fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 1968fbbfccdSPaul Luse 1978fbbfccdSPaul Luse if (bserrno) { 1988fbbfccdSPaul Luse printf("Error %d unloading the bobstore\n", bserrno); 1998fbbfccdSPaul Luse cli_context->rc = bserrno; 2008fbbfccdSPaul Luse } 2018fbbfccdSPaul Luse 202c5e63c71SPaul Luse /* 203c5e63c71SPaul Luse * Quit if we're in cmd mode or exiting shell mode, otherwise 204c5e63c71SPaul Luse * clear the action field and start the main function again. 205c5e63c71SPaul Luse */ 206c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD || 207c5e63c71SPaul Luse cli_context->action == CLI_SHELL_EXIT) { 2088fbbfccdSPaul Luse spdk_app_stop(cli_context->rc); 209c5e63c71SPaul Luse } else { 210d4e565b4SPaul Luse /* when action is CLI_NONE, we know we need to remain in the shell */ 211d4e565b4SPaul Luse cli_context->bs = NULL; 212c5e63c71SPaul Luse cli_context->action = CLI_NONE; 213deb8ee5cSBen Walker cli_start(cli_context); 214c5e63c71SPaul Luse } 2158fbbfccdSPaul Luse } 2168fbbfccdSPaul Luse 2178fbbfccdSPaul Luse /* 2188fbbfccdSPaul Luse * Unload the blobstore. 2198fbbfccdSPaul Luse */ 2208fbbfccdSPaul Luse static void 2218fbbfccdSPaul Luse unload_bs(struct cli_context_t *cli_context, char *msg, int bserrno) 2228fbbfccdSPaul Luse { 2238fbbfccdSPaul Luse if (bserrno) { 2248fbbfccdSPaul Luse printf("%s (err %d)\n", msg, bserrno); 2258fbbfccdSPaul Luse cli_context->rc = bserrno; 2268fbbfccdSPaul Luse } 227c5e63c71SPaul Luse 2288fbbfccdSPaul Luse if (cli_context->bs) { 229c5e63c71SPaul Luse if (cli_context->channel) { 230c5e63c71SPaul Luse spdk_bs_free_io_channel(cli_context->channel); 231d4e565b4SPaul Luse cli_context->channel = NULL; 232c5e63c71SPaul Luse } 2338fbbfccdSPaul Luse spdk_bs_unload(cli_context->bs, unload_complete, cli_context); 234a23fa64cSPaul Luse } else if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 2358fbbfccdSPaul Luse spdk_app_stop(bserrno); 236a23fa64cSPaul Luse 2378fbbfccdSPaul Luse } 2388fbbfccdSPaul Luse } 2398fbbfccdSPaul Luse 2408fbbfccdSPaul Luse /* 2418fbbfccdSPaul Luse * Callback for closing a blob. 2428fbbfccdSPaul Luse */ 2438fbbfccdSPaul Luse static void 2448fbbfccdSPaul Luse close_cb(void *arg1, int bserrno) 2458fbbfccdSPaul Luse { 2468fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 2478fbbfccdSPaul Luse 2488fbbfccdSPaul Luse if (bserrno) { 2498fbbfccdSPaul Luse unload_bs(cli_context, "Error in close callback", 2508fbbfccdSPaul Luse bserrno); 2518fbbfccdSPaul Luse return; 2528fbbfccdSPaul Luse } 2538fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 2548fbbfccdSPaul Luse } 2558fbbfccdSPaul Luse 2568fbbfccdSPaul Luse /* 257b7876f9aSJosh Soref * Callback function for syncing metadata. 2588fbbfccdSPaul Luse */ 2598fbbfccdSPaul Luse static void 260866f15b3SPaul Luse sync_cb(void *arg1, int bserrno) 2618fbbfccdSPaul Luse { 2628fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 2638fbbfccdSPaul Luse 2648fbbfccdSPaul Luse if (bserrno) { 2658fbbfccdSPaul Luse unload_bs(cli_context, "Error in sync callback", 2668fbbfccdSPaul Luse bserrno); 2678fbbfccdSPaul Luse return; 2688fbbfccdSPaul Luse } 2698fbbfccdSPaul Luse 270e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 2718fbbfccdSPaul Luse } 2728fbbfccdSPaul Luse 2738fbbfccdSPaul Luse static void 274463925ffSJim Harris resize_cb(void *cb_arg, int bserrno) 2758fbbfccdSPaul Luse { 2768fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 2778fbbfccdSPaul Luse uint64_t total = 0; 2788fbbfccdSPaul Luse 2798fbbfccdSPaul Luse if (bserrno) { 2808fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob resize", 2818fbbfccdSPaul Luse bserrno); 2828fbbfccdSPaul Luse return; 2838fbbfccdSPaul Luse } 2848fbbfccdSPaul Luse 2858fbbfccdSPaul Luse total = spdk_blob_get_num_clusters(cli_context->blob); 2868fbbfccdSPaul Luse printf("blob now has USED clusters of %" PRIu64 "\n", 2878fbbfccdSPaul Luse total); 2888fbbfccdSPaul Luse 2898fbbfccdSPaul Luse /* 2908fbbfccdSPaul Luse * Always a good idea to sync after MD changes or the changes 2918fbbfccdSPaul Luse * may be lost if things aren't closed cleanly. 2928fbbfccdSPaul Luse */ 2932c3591f1SJim Harris spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context); 2948fbbfccdSPaul Luse } 2958fbbfccdSPaul Luse 2968fbbfccdSPaul Luse /* 297463925ffSJim Harris * Callback function for opening a blob after creating. 298463925ffSJim Harris */ 299463925ffSJim Harris static void 300463925ffSJim Harris open_now_resize_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 301463925ffSJim Harris { 302463925ffSJim Harris struct cli_context_t *cli_context = cb_arg; 303463925ffSJim Harris 304463925ffSJim Harris if (bserrno) { 305463925ffSJim Harris unload_bs(cli_context, "Error in open completion", 306463925ffSJim Harris bserrno); 307463925ffSJim Harris return; 308463925ffSJim Harris } 309463925ffSJim Harris cli_context->blob = blob; 310463925ffSJim Harris 311463925ffSJim Harris spdk_blob_resize(cli_context->blob, cli_context->num_clusters, 312463925ffSJim Harris resize_cb, cli_context); 313463925ffSJim Harris } 314463925ffSJim Harris 315463925ffSJim Harris /* 3168fbbfccdSPaul Luse * Callback function for creating a blob. 3178fbbfccdSPaul Luse */ 3188fbbfccdSPaul Luse static void 319866f15b3SPaul Luse blob_create_cb(void *arg1, spdk_blob_id blobid, int bserrno) 3208fbbfccdSPaul Luse { 3218fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 3228fbbfccdSPaul Luse 3238fbbfccdSPaul Luse if (bserrno) { 3248fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob create callback", 3258fbbfccdSPaul Luse bserrno); 3268fbbfccdSPaul Luse return; 3278fbbfccdSPaul Luse } 3288fbbfccdSPaul Luse 3298fbbfccdSPaul Luse cli_context->blobid = blobid; 33076a577b0SMike Gerdts printf("New blob id 0x%" PRIx64 "\n", cli_context->blobid); 3318fbbfccdSPaul Luse 332e5044f79SPaul Luse /* if we're in script mode, we need info on all blobids for later */ 333e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 334e5044f79SPaul Luse g_script.blobid[g_script.blobid_idx++] = blobid; 335e5044f79SPaul Luse } 336e5044f79SPaul Luse 3378fbbfccdSPaul Luse /* We have to open the blob before we can do things like resize. */ 338d52dbda2SJim Harris spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 339866f15b3SPaul Luse open_now_resize_cb, cli_context); 3408fbbfccdSPaul Luse } 3418fbbfccdSPaul Luse 3428fbbfccdSPaul Luse /* 3438fbbfccdSPaul Luse * Callback for get_super where we'll continue on to show blobstore info. 3448fbbfccdSPaul Luse */ 3458fbbfccdSPaul Luse static void 346866f15b3SPaul Luse show_bs_cb(void *arg1, spdk_blob_id blobid, int bserrno) 3478fbbfccdSPaul Luse { 3488fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 349b3781b22SDaniel Verkamp struct spdk_bs_type bstype; 3508fbbfccdSPaul Luse uint64_t val; 3518fbbfccdSPaul Luse struct spdk_bdev *bdev = NULL; 3528fbbfccdSPaul Luse 3538fbbfccdSPaul Luse if (bserrno && bserrno != -ENOENT) { 3548fbbfccdSPaul Luse unload_bs(cli_context, "Error in get_super callback", 3558fbbfccdSPaul Luse bserrno); 3568fbbfccdSPaul Luse return; 3578fbbfccdSPaul Luse } 3588fbbfccdSPaul Luse cli_context->superid = blobid; 3598fbbfccdSPaul Luse 3608fbbfccdSPaul Luse bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 3618fbbfccdSPaul Luse if (bdev == NULL) { 3628fbbfccdSPaul Luse unload_bs(cli_context, "Error w/bdev in get_super callback", 3638fbbfccdSPaul Luse bserrno); 3648fbbfccdSPaul Luse return; 3658fbbfccdSPaul Luse } 3668fbbfccdSPaul Luse 3678fbbfccdSPaul Luse printf("Blobstore Public Info:\n"); 3688fbbfccdSPaul Luse printf("\tUsing bdev Product Name: %s\n", 3698fbbfccdSPaul Luse spdk_bdev_get_product_name(bdev)); 3708fbbfccdSPaul Luse printf("\tAPI Version: %d\n", SPDK_BS_VERSION); 3718fbbfccdSPaul Luse 3728fbbfccdSPaul Luse if (bserrno != -ENOENT) { 37376a577b0SMike Gerdts printf("\tsuper blob ID: 0x%" PRIx64 "\n", cli_context->superid); 3748fbbfccdSPaul Luse } else { 3758fbbfccdSPaul Luse printf("\tsuper blob ID: none assigned\n"); 3768fbbfccdSPaul Luse } 3778fbbfccdSPaul Luse 3783299bf6dSJim Harris printf("\tmd page size: %" PRIu64 "\n", cli_context->page_size); 3796751b44bSTomasz Zawadzki printf("\tio unit size: %" PRIu64 "\n", cli_context->io_unit_size); 3808fbbfccdSPaul Luse 3818fbbfccdSPaul Luse val = spdk_bs_get_cluster_size(cli_context->bs); 3828fbbfccdSPaul Luse printf("\tcluster size: %" PRIu64 "\n", val); 3838fbbfccdSPaul Luse 3848fbbfccdSPaul Luse val = spdk_bs_free_cluster_count(cli_context->bs); 3858fbbfccdSPaul Luse printf("\t# free clusters: %" PRIu64 "\n", val); 3868fbbfccdSPaul Luse 387b3781b22SDaniel Verkamp bstype = spdk_bs_get_bstype(cli_context->bs); 388b4b7d5d3SJim Harris spdk_log_dump(stdout, "\tblobstore type:", &bstype, sizeof(bstype)); 389b3781b22SDaniel Verkamp 3908fbbfccdSPaul Luse /* 3918fbbfccdSPaul Luse * Private info isn't accessible via the public API but 3928fbbfccdSPaul Luse * may be useful for debug of blobstore based applications. 3938fbbfccdSPaul Luse */ 3948fbbfccdSPaul Luse printf("\nBlobstore Private Info:\n"); 3958fbbfccdSPaul Luse printf("\tMetadata start (pages): %" PRIu64 "\n", 3968fbbfccdSPaul Luse cli_context->bs->md_start); 3978fbbfccdSPaul Luse printf("\tMetadata length (pages): %d\n", 3988fbbfccdSPaul Luse cli_context->bs->md_len); 3998fbbfccdSPaul Luse 4008fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 4018fbbfccdSPaul Luse } 4028fbbfccdSPaul Luse 4038fbbfccdSPaul Luse /* 4048fbbfccdSPaul Luse * Show detailed info about a particular blob. 4058fbbfccdSPaul Luse */ 4068fbbfccdSPaul Luse static void 4078fbbfccdSPaul Luse show_blob(struct cli_context_t *cli_context) 4088fbbfccdSPaul Luse { 4098fbbfccdSPaul Luse uint64_t val; 4108fbbfccdSPaul Luse struct spdk_xattr_names *names; 4118fbbfccdSPaul Luse const void *value; 4128fbbfccdSPaul Luse size_t value_len; 4138fbbfccdSPaul Luse unsigned int i; 4148fbbfccdSPaul Luse 4158fbbfccdSPaul Luse printf("Blob Public Info:\n"); 4168fbbfccdSPaul Luse 41776a577b0SMike Gerdts printf("blob ID: 0x%" PRIx64 "\n", cli_context->blobid); 4188fbbfccdSPaul Luse 4198fbbfccdSPaul Luse val = spdk_blob_get_num_clusters(cli_context->blob); 4208fbbfccdSPaul Luse printf("# of clusters: %" PRIu64 "\n", val); 4218fbbfccdSPaul Luse 4228fbbfccdSPaul Luse printf("# of bytes: %" PRIu64 "\n", 4238fbbfccdSPaul Luse val * spdk_bs_get_cluster_size(cli_context->bs)); 4248fbbfccdSPaul Luse 4253299bf6dSJim Harris val = spdk_blob_get_num_io_units(cli_context->blob); 4263299bf6dSJim Harris printf("# of io units: %" PRIu64 "\n", val); 4278fbbfccdSPaul Luse 4282c3591f1SJim Harris spdk_blob_get_xattr_names(cli_context->blob, &names); 4298fbbfccdSPaul Luse 4308fbbfccdSPaul Luse printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names)); 4318fbbfccdSPaul Luse printf("xattrs:\n"); 4328fbbfccdSPaul Luse for (i = 0; i < spdk_xattr_names_get_count(names); i++) { 4332c3591f1SJim Harris spdk_blob_get_xattr_value(cli_context->blob, 4348fbbfccdSPaul Luse spdk_xattr_names_get_name(names, i), 4358fbbfccdSPaul Luse &value, &value_len); 43613d6d003SRichael Zhuang if (value_len > BUFSIZE) { 4378fbbfccdSPaul Luse printf("FYI: adjusting size of xattr due to CLI limits.\n"); 43813d6d003SRichael Zhuang value_len = BUFSIZE + 1; 4398fbbfccdSPaul Luse } 4408fbbfccdSPaul Luse printf("\n(%d) Name:%s\n", i, 4418fbbfccdSPaul Luse spdk_xattr_names_get_name(names, i)); 4428fbbfccdSPaul Luse printf("(%d) Value:\n", i); 44313d6d003SRichael Zhuang spdk_log_dump(stdout, "", value, value_len - 1); 4448fbbfccdSPaul Luse } 4458fbbfccdSPaul Luse 4468fbbfccdSPaul Luse /* 4478fbbfccdSPaul Luse * Private info isn't accessible via the public API but 4488fbbfccdSPaul Luse * may be useful for debug of blobstore based applications. 4498fbbfccdSPaul Luse */ 4508fbbfccdSPaul Luse printf("\nBlob Private Info:\n"); 451c8efd8a8SJim Harris switch (cli_context->blob->state) { 4528fbbfccdSPaul Luse case SPDK_BLOB_STATE_DIRTY: 4538fbbfccdSPaul Luse printf("state: DIRTY\n"); 4548fbbfccdSPaul Luse break; 4558fbbfccdSPaul Luse case SPDK_BLOB_STATE_CLEAN: 4568fbbfccdSPaul Luse printf("state: CLEAN\n"); 4578fbbfccdSPaul Luse break; 4588fbbfccdSPaul Luse case SPDK_BLOB_STATE_LOADING: 4598fbbfccdSPaul Luse printf("state: LOADING\n"); 4608fbbfccdSPaul Luse break; 4618fbbfccdSPaul Luse default: 4628fbbfccdSPaul Luse printf("state: UNKNOWN\n"); 4638fbbfccdSPaul Luse break; 4648fbbfccdSPaul Luse } 4658fbbfccdSPaul Luse printf("open ref count: %d\n", 466c8efd8a8SJim Harris cli_context->blob->open_ref); 467c5e63c71SPaul Luse 468c5e63c71SPaul Luse spdk_xattr_names_free(names); 4698fbbfccdSPaul Luse } 4708fbbfccdSPaul Luse 4718fbbfccdSPaul Luse /* 472866f15b3SPaul Luse * Callback for getting the first blob, shared with simple blob listing as well. 4738fbbfccdSPaul Luse */ 4748fbbfccdSPaul Luse static void 4758fbbfccdSPaul Luse blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno) 4768fbbfccdSPaul Luse { 4778fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 4788fbbfccdSPaul Luse 4798fbbfccdSPaul Luse if (bserrno) { 4808fbbfccdSPaul Luse if (bserrno == -ENOENT) { 4818fbbfccdSPaul Luse /* this simply means there are no more blobs */ 4828fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 4838fbbfccdSPaul Luse } else { 4848fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob iter callback", 4858fbbfccdSPaul Luse bserrno); 4868fbbfccdSPaul Luse } 4878fbbfccdSPaul Luse return; 4888fbbfccdSPaul Luse } 4898fbbfccdSPaul Luse 4908fbbfccdSPaul Luse if (cli_context->action == CLI_LIST_BLOBS) { 491866f15b3SPaul Luse printf("\nList BLOBS:\n"); 49276a577b0SMike Gerdts printf("Found blob with ID# 0x%" PRIx64 "\n", 4938fbbfccdSPaul Luse spdk_blob_get_id(blob)); 4948fbbfccdSPaul Luse } else if (spdk_blob_get_id(blob) == cli_context->blobid) { 4958fbbfccdSPaul Luse /* 4968fbbfccdSPaul Luse * Found the blob we're looking for, but we need to finish 4978fbbfccdSPaul Luse * iterating even after showing the info so that internally 4988fbbfccdSPaul Luse * the blobstore logic will close the blob. Or we could 4998fbbfccdSPaul Luse * chose to close it now, either way. 5008fbbfccdSPaul Luse */ 5018fbbfccdSPaul Luse cli_context->blob = blob; 5028fbbfccdSPaul Luse show_blob(cli_context); 5038fbbfccdSPaul Luse } 5048fbbfccdSPaul Luse 505ae5a01ddSJim Harris spdk_bs_iter_next(cli_context->bs, blob, blob_iter_cb, cli_context); 5068fbbfccdSPaul Luse } 5078fbbfccdSPaul Luse 5088fbbfccdSPaul Luse /* 5098fbbfccdSPaul Luse * Callback for setting the super blob ID. 5108fbbfccdSPaul Luse */ 5118fbbfccdSPaul Luse static void 5128fbbfccdSPaul Luse set_super_cb(void *arg1, int bserrno) 5138fbbfccdSPaul Luse { 5148fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 5158fbbfccdSPaul Luse 5168fbbfccdSPaul Luse if (bserrno) { 5178fbbfccdSPaul Luse unload_bs(cli_context, "Error in set_super callback", 5188fbbfccdSPaul Luse bserrno); 5198fbbfccdSPaul Luse return; 5208fbbfccdSPaul Luse } 5218fbbfccdSPaul Luse 5228fbbfccdSPaul Luse printf("Super Blob ID has been set.\n"); 5238fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 5248fbbfccdSPaul Luse } 5258fbbfccdSPaul Luse 5268fbbfccdSPaul Luse /* 5278fbbfccdSPaul Luse * Callback for set_xattr_open where we set or delete xattrs. 5288fbbfccdSPaul Luse */ 5298fbbfccdSPaul Luse static void 530866f15b3SPaul Luse set_xattr_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 5318fbbfccdSPaul Luse { 5328fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 5338fbbfccdSPaul Luse 5348fbbfccdSPaul Luse if (bserrno) { 5358fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob open callback", 5368fbbfccdSPaul Luse bserrno); 5378fbbfccdSPaul Luse return; 5388fbbfccdSPaul Luse } 5398fbbfccdSPaul Luse cli_context->blob = blob; 5408fbbfccdSPaul Luse 5418fbbfccdSPaul Luse if (cli_context->action == CLI_SET_XATTR) { 5422c3591f1SJim Harris spdk_blob_set_xattr(cli_context->blob, cli_context->key, 5432c3591f1SJim Harris cli_context->value, strlen(cli_context->value) + 1); 5448fbbfccdSPaul Luse printf("Xattr has been set.\n"); 5458fbbfccdSPaul Luse } else { 5462c3591f1SJim Harris spdk_blob_remove_xattr(cli_context->blob, cli_context->key); 5478fbbfccdSPaul Luse printf("Xattr has been removed.\n"); 5488fbbfccdSPaul Luse } 5498fbbfccdSPaul Luse 5502c3591f1SJim Harris spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context); 5518fbbfccdSPaul Luse } 5528fbbfccdSPaul Luse 553f45c98ddSJim Harris static void __read_dump_cb(void *arg1); 554f45c98ddSJim Harris 5558fbbfccdSPaul Luse /* 5568fbbfccdSPaul Luse * Callback function for reading a blob for dumping to a file. 5578fbbfccdSPaul Luse */ 5588fbbfccdSPaul Luse static void 559866f15b3SPaul Luse read_dump_cb(void *arg1, int bserrno) 5608fbbfccdSPaul Luse { 5618fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 5628fbbfccdSPaul Luse uint64_t bytes_written; 5638fbbfccdSPaul Luse 5648fbbfccdSPaul Luse if (bserrno) { 5658fbbfccdSPaul Luse fclose(cli_context->fp); 5668fbbfccdSPaul Luse unload_bs(cli_context, "Error in read completion", 5678fbbfccdSPaul Luse bserrno); 5688fbbfccdSPaul Luse return; 5698fbbfccdSPaul Luse } 5708fbbfccdSPaul Luse 5716751b44bSTomasz Zawadzki bytes_written = fwrite(cli_context->buff, NUM_IO_UNITS, cli_context->io_unit_size, 5728fbbfccdSPaul Luse cli_context->fp); 5736751b44bSTomasz Zawadzki if (bytes_written != cli_context->io_unit_size) { 5748fbbfccdSPaul Luse fclose(cli_context->fp); 5758fbbfccdSPaul Luse unload_bs(cli_context, "Error with fwrite", 5768fbbfccdSPaul Luse bserrno); 5778fbbfccdSPaul Luse return; 5788fbbfccdSPaul Luse } 5798fbbfccdSPaul Luse 580f45c98ddSJim Harris /* This completion may have occurred in the context of a read to 581f45c98ddSJim Harris * an unallocated cluster. So we can't issue the next read here, or 582f45c98ddSJim Harris * we risk overflowing the stack. So use spdk_thread_send_msg() to 583f45c98ddSJim Harris * make sure we unwind before doing the next read. 584f45c98ddSJim Harris */ 585f45c98ddSJim Harris spdk_thread_send_msg(spdk_get_thread(), __read_dump_cb, cli_context); 586f45c98ddSJim Harris } 587f45c98ddSJim Harris 588f45c98ddSJim Harris static void 589f45c98ddSJim Harris __read_dump_cb(void *arg1) 590f45c98ddSJim Harris { 591f45c98ddSJim Harris struct cli_context_t *cli_context = arg1; 592f45c98ddSJim Harris 5938fbbfccdSPaul Luse printf("."); 5946751b44bSTomasz Zawadzki if (++cli_context->io_unit_count < cli_context->blob_io_units) { 5958fbbfccdSPaul Luse /* perform another read */ 59666fc591fSJim Harris spdk_blob_io_read(cli_context->blob, cli_context->channel, 5976751b44bSTomasz Zawadzki cli_context->buff, cli_context->io_unit_count, 5986751b44bSTomasz Zawadzki NUM_IO_UNITS, read_dump_cb, cli_context); 5998fbbfccdSPaul Luse } else { 6008fbbfccdSPaul Luse /* done reading */ 601e9a50eb8SPaul Luse printf("\nFile write complete (to %s).\n", cli_context->file); 6028fbbfccdSPaul Luse fclose(cli_context->fp); 603e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 6048fbbfccdSPaul Luse } 6058fbbfccdSPaul Luse } 6068fbbfccdSPaul Luse 6078fbbfccdSPaul Luse /* 6088fbbfccdSPaul Luse * Callback for write completion on the import of a file to a blob. 6098fbbfccdSPaul Luse */ 6108fbbfccdSPaul Luse static void 611866f15b3SPaul Luse write_imp_cb(void *arg1, int bserrno) 6128fbbfccdSPaul Luse { 6138fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 6148fbbfccdSPaul Luse uint64_t bytes_read; 6158fbbfccdSPaul Luse 6168fbbfccdSPaul Luse if (bserrno) { 6178fbbfccdSPaul Luse fclose(cli_context->fp); 6188fbbfccdSPaul Luse unload_bs(cli_context, "Error in write completion", 6198fbbfccdSPaul Luse bserrno); 6208fbbfccdSPaul Luse return; 6218fbbfccdSPaul Luse } 6228fbbfccdSPaul Luse 6238fbbfccdSPaul Luse if (cli_context->bytes_so_far < cli_context->filesize) { 6248fbbfccdSPaul Luse /* perform another file read */ 6258fbbfccdSPaul Luse bytes_read = fread(cli_context->buff, 1, 6266751b44bSTomasz Zawadzki cli_context->io_unit_size, 6278fbbfccdSPaul Luse cli_context->fp); 6288fbbfccdSPaul Luse cli_context->bytes_so_far += bytes_read; 6298fbbfccdSPaul Luse 6306751b44bSTomasz Zawadzki /* if this read is < 1 io_unit, fill with 0s */ 6316751b44bSTomasz Zawadzki if (bytes_read < cli_context->io_unit_size) { 6328fbbfccdSPaul Luse uint8_t *offset = cli_context->buff + bytes_read; 6336751b44bSTomasz Zawadzki memset(offset, 0, cli_context->io_unit_size - bytes_read); 6348fbbfccdSPaul Luse } 6358fbbfccdSPaul Luse } else { 6368fbbfccdSPaul Luse /* 6378fbbfccdSPaul Luse * Done reading the file, fill the rest of the blob with 0s, 6386751b44bSTomasz Zawadzki * yeah we're memsetting the same io_unit over and over here 6398fbbfccdSPaul Luse */ 6406751b44bSTomasz Zawadzki memset(cli_context->buff, 0, cli_context->io_unit_size); 6418fbbfccdSPaul Luse } 6426751b44bSTomasz Zawadzki if (++cli_context->io_unit_count < cli_context->blob_io_units) { 6438fbbfccdSPaul Luse printf("."); 64466fc591fSJim Harris spdk_blob_io_write(cli_context->blob, cli_context->channel, 6456751b44bSTomasz Zawadzki cli_context->buff, cli_context->io_unit_count, 6466751b44bSTomasz Zawadzki NUM_IO_UNITS, write_imp_cb, cli_context); 6478fbbfccdSPaul Luse } else { 6488fbbfccdSPaul Luse /* done writing */ 649e9a50eb8SPaul Luse printf("\nBlob import complete (from %s).\n", cli_context->file); 6508fbbfccdSPaul Luse fclose(cli_context->fp); 651e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 6528fbbfccdSPaul Luse } 6538fbbfccdSPaul Luse } 6548fbbfccdSPaul Luse 6558fbbfccdSPaul Luse /* 6568fbbfccdSPaul Luse * Callback for open blobs where we'll continue on dump a blob to a file or 6578fbbfccdSPaul Luse * import a file to a blob. For dump, the resulting file will always be the 6588fbbfccdSPaul Luse * full size of the blob. For import, the blob will fill with the file 6598fbbfccdSPaul Luse * contents first and then 0 out the rest of the blob. 6608fbbfccdSPaul Luse */ 6618fbbfccdSPaul Luse static void 662866f15b3SPaul Luse dump_imp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 6638fbbfccdSPaul Luse { 6648fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 6658fbbfccdSPaul Luse 6668fbbfccdSPaul Luse if (bserrno) { 6678fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob open callback", 6688fbbfccdSPaul Luse bserrno); 6698fbbfccdSPaul Luse return; 6708fbbfccdSPaul Luse } 6718fbbfccdSPaul Luse cli_context->blob = blob; 6728fbbfccdSPaul Luse 6738fbbfccdSPaul Luse /* 6746751b44bSTomasz Zawadzki * We'll transfer just one io_unit at a time to keep the buffer 6758fbbfccdSPaul Luse * small. This could be bigger of course. 6768fbbfccdSPaul Luse */ 67727a23a33SDarek Stojaczyk cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL, 67827a23a33SDarek Stojaczyk SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 6798fbbfccdSPaul Luse if (cli_context->buff == NULL) { 680809dc418SPaul Luse printf("Error in allocating memory\n"); 681e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 6828fbbfccdSPaul Luse return; 6838fbbfccdSPaul Luse } 6848fbbfccdSPaul Luse printf("Working"); 6856751b44bSTomasz Zawadzki cli_context->blob_io_units = spdk_blob_get_num_io_units(cli_context->blob); 6866751b44bSTomasz Zawadzki cli_context->io_unit_count = 0; 6874952f254SDaniel Verkamp if (cli_context->action == CLI_DUMP_BLOB) { 6888fbbfccdSPaul Luse cli_context->fp = fopen(cli_context->file, "w"); 689809dc418SPaul Luse if (cli_context->fp == NULL) { 690809dc418SPaul Luse printf("Error in opening file\n"); 691e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 692809dc418SPaul Luse return; 693809dc418SPaul Luse } 6948fbbfccdSPaul Luse 6958389b65aSMike Gerdts if (cli_context->blob->active.num_clusters == 0) { 6968389b65aSMike Gerdts fclose(cli_context->fp); 6978389b65aSMike Gerdts spdk_blob_close(cli_context->blob, close_cb, cli_context); 6988389b65aSMike Gerdts return; 6998389b65aSMike Gerdts } 7008389b65aSMike Gerdts 7016751b44bSTomasz Zawadzki /* read a io_unit of data from the blob */ 70266fc591fSJim Harris spdk_blob_io_read(cli_context->blob, cli_context->channel, 7036751b44bSTomasz Zawadzki cli_context->buff, cli_context->io_unit_count, 7046751b44bSTomasz Zawadzki NUM_IO_UNITS, read_dump_cb, cli_context); 7058fbbfccdSPaul Luse } else { 7068fbbfccdSPaul Luse cli_context->fp = fopen(cli_context->file, "r"); 707809dc418SPaul Luse if (cli_context->fp == NULL) { 708a0e22e13Spaul luse printf("Error in opening file: errno %d\n", errno); 709e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 710809dc418SPaul Luse return; 711809dc418SPaul Luse } 7128fbbfccdSPaul Luse 7136751b44bSTomasz Zawadzki /* get the filesize then rewind read a io_unit of data from file */ 7148fbbfccdSPaul Luse fseek(cli_context->fp, 0L, SEEK_END); 7158fbbfccdSPaul Luse cli_context->filesize = ftell(cli_context->fp); 7168fbbfccdSPaul Luse rewind(cli_context->fp); 7176751b44bSTomasz Zawadzki cli_context->bytes_so_far = fread(cli_context->buff, NUM_IO_UNITS, 7186751b44bSTomasz Zawadzki cli_context->io_unit_size, 7198fbbfccdSPaul Luse cli_context->fp); 7208fbbfccdSPaul Luse 7216751b44bSTomasz Zawadzki /* if the file is < a io_unit, fill the rest with 0s */ 7226751b44bSTomasz Zawadzki if (cli_context->filesize < cli_context->io_unit_size) { 7238fbbfccdSPaul Luse uint8_t *offset = 7248fbbfccdSPaul Luse cli_context->buff + cli_context->filesize; 7258fbbfccdSPaul Luse 7268fbbfccdSPaul Luse memset(offset, 0, 7276751b44bSTomasz Zawadzki cli_context->io_unit_size - cli_context->filesize); 7288fbbfccdSPaul Luse } 7298fbbfccdSPaul Luse 73066fc591fSJim Harris spdk_blob_io_write(cli_context->blob, cli_context->channel, 7316751b44bSTomasz Zawadzki cli_context->buff, cli_context->io_unit_count, 7326751b44bSTomasz Zawadzki NUM_IO_UNITS, write_imp_cb, cli_context); 7338fbbfccdSPaul Luse } 7348fbbfccdSPaul Luse } 7358fbbfccdSPaul Luse 7368fbbfccdSPaul Luse /* 7376751b44bSTomasz Zawadzki * Callback function for writing a specific pattern to io_unit 0. 7388fbbfccdSPaul Luse */ 7398fbbfccdSPaul Luse static void 740866f15b3SPaul Luse write_cb(void *arg1, int bserrno) 7418fbbfccdSPaul Luse { 7428fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 7438fbbfccdSPaul Luse 7448fbbfccdSPaul Luse if (bserrno) { 7458fbbfccdSPaul Luse unload_bs(cli_context, "Error in write completion", 7468fbbfccdSPaul Luse bserrno); 7478fbbfccdSPaul Luse return; 7488fbbfccdSPaul Luse } 7498fbbfccdSPaul Luse printf("."); 7506751b44bSTomasz Zawadzki if (++cli_context->io_unit_count < cli_context->blob_io_units) { 75166fc591fSJim Harris spdk_blob_io_write(cli_context->blob, cli_context->channel, 7526751b44bSTomasz Zawadzki cli_context->buff, cli_context->io_unit_count, 7536751b44bSTomasz Zawadzki NUM_IO_UNITS, write_cb, cli_context); 7548fbbfccdSPaul Luse } else { 7558fbbfccdSPaul Luse /* done writing */ 756e9a50eb8SPaul Luse printf("\nBlob fill complete (with 0x%x).\n", cli_context->fill_value); 757e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 7588fbbfccdSPaul Luse } 7598fbbfccdSPaul Luse } 7608fbbfccdSPaul Luse 7618fbbfccdSPaul Luse /* 762866f15b3SPaul Luse * Callback function to fill a blob with a value, callback from open. 7638fbbfccdSPaul Luse */ 7648fbbfccdSPaul Luse static void 765866f15b3SPaul Luse fill_blob_cb(void *arg1, struct spdk_blob *blob, int bserrno) 7668fbbfccdSPaul Luse { 767866f15b3SPaul Luse struct cli_context_t *cli_context = arg1; 7688fbbfccdSPaul Luse 7698fbbfccdSPaul Luse if (bserrno) { 770866f15b3SPaul Luse unload_bs(cli_context, "Error in open callback", 7718fbbfccdSPaul Luse bserrno); 7728fbbfccdSPaul Luse return; 7738fbbfccdSPaul Luse } 774866f15b3SPaul Luse 7758fbbfccdSPaul Luse cli_context->blob = blob; 7766751b44bSTomasz Zawadzki cli_context->io_unit_count = 0; 7776751b44bSTomasz Zawadzki cli_context->blob_io_units = spdk_blob_get_num_io_units(cli_context->blob); 77827a23a33SDarek Stojaczyk cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL, 77927a23a33SDarek Stojaczyk SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 7808fbbfccdSPaul Luse if (cli_context->buff == NULL) { 7818fbbfccdSPaul Luse unload_bs(cli_context, "Error in allocating memory", 7828fbbfccdSPaul Luse -ENOMEM); 7838fbbfccdSPaul Luse return; 7848fbbfccdSPaul Luse } 7858fbbfccdSPaul Luse 7868fbbfccdSPaul Luse memset(cli_context->buff, cli_context->fill_value, 7876751b44bSTomasz Zawadzki cli_context->io_unit_size); 788e5044f79SPaul Luse printf("Working"); 78966fc591fSJim Harris spdk_blob_io_write(cli_context->blob, cli_context->channel, 7908fbbfccdSPaul Luse cli_context->buff, 7916751b44bSTomasz Zawadzki STARTING_IO_UNIT, NUM_IO_UNITS, write_cb, cli_context); 7928fbbfccdSPaul Luse } 7938fbbfccdSPaul Luse 7948fbbfccdSPaul Luse /* 7953453391bSMike Gerdts * Callback for deleting a blob 7963453391bSMike Gerdts */ 7973453391bSMike Gerdts static void 7983453391bSMike Gerdts delete_blob_cb(void *arg1, int bserrno) 7993453391bSMike Gerdts { 8003453391bSMike Gerdts struct cli_context_t *cli_context = arg1; 8013453391bSMike Gerdts 8023453391bSMike Gerdts if (bserrno) { 8033453391bSMike Gerdts unload_bs(cli_context, "Error in delete_blob callback", 8043453391bSMike Gerdts bserrno); 8053453391bSMike Gerdts return; 8063453391bSMike Gerdts } 8073453391bSMike Gerdts 8083453391bSMike Gerdts printf("Blob 0x%lx has been deleted.\n", cli_context->blobid); 8093453391bSMike Gerdts unload_bs(cli_context, "", 0); 8103453391bSMike Gerdts } 8113453391bSMike Gerdts 8123453391bSMike Gerdts /* 813866f15b3SPaul Luse * Multiple actions require us to open the bs first so here we use 814866f15b3SPaul Luse * a common callback to set a bunch of values and then move on to 815866f15b3SPaul Luse * the next step saved off via function pointer. 8168fbbfccdSPaul Luse */ 8178fbbfccdSPaul Luse static void 818866f15b3SPaul Luse load_bs_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 8198fbbfccdSPaul Luse { 8208fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 8218fbbfccdSPaul Luse 8228fbbfccdSPaul Luse if (bserrno) { 8238fbbfccdSPaul Luse unload_bs(cli_context, "Error in load callback", 8248fbbfccdSPaul Luse bserrno); 8258fbbfccdSPaul Luse return; 8268fbbfccdSPaul Luse } 827866f15b3SPaul Luse 8288fbbfccdSPaul Luse cli_context->bs = bs; 8298fbbfccdSPaul Luse cli_context->page_size = spdk_bs_get_page_size(cli_context->bs); 8306751b44bSTomasz Zawadzki cli_context->io_unit_size = spdk_bs_get_io_unit_size(cli_context->bs); 8318fbbfccdSPaul Luse cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs); 8328fbbfccdSPaul Luse if (cli_context->channel == NULL) { 8338fbbfccdSPaul Luse unload_bs(cli_context, "Error in allocating channel", 8348fbbfccdSPaul Luse -ENOMEM); 8358fbbfccdSPaul Luse return; 8368fbbfccdSPaul Luse } 8378fbbfccdSPaul Luse 838866f15b3SPaul Luse switch (cli_context->action) { 839866f15b3SPaul Luse case CLI_SET_SUPER: 840866f15b3SPaul Luse spdk_bs_set_super(cli_context->bs, cli_context->superid, 841866f15b3SPaul Luse set_super_cb, cli_context); 842866f15b3SPaul Luse break; 843866f15b3SPaul Luse case CLI_SHOW_BS: 844866f15b3SPaul Luse spdk_bs_get_super(cli_context->bs, show_bs_cb, cli_context); 845866f15b3SPaul Luse break; 846866f15b3SPaul Luse case CLI_CREATE_BLOB: 847d52dbda2SJim Harris spdk_bs_create_blob(cli_context->bs, blob_create_cb, cli_context); 848866f15b3SPaul Luse break; 849866f15b3SPaul Luse case CLI_SET_XATTR: 850866f15b3SPaul Luse case CLI_REM_XATTR: 851d52dbda2SJim Harris spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 852866f15b3SPaul Luse set_xattr_cb, cli_context); 853866f15b3SPaul Luse break; 854866f15b3SPaul Luse case CLI_SHOW_BLOB: 855866f15b3SPaul Luse case CLI_LIST_BLOBS: 856d52dbda2SJim Harris spdk_bs_iter_first(cli_context->bs, blob_iter_cb, cli_context); 857866f15b3SPaul Luse 858866f15b3SPaul Luse break; 8594952f254SDaniel Verkamp case CLI_DUMP_BLOB: 8604952f254SDaniel Verkamp case CLI_IMPORT_BLOB: 861d52dbda2SJim Harris spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 862866f15b3SPaul Luse dump_imp_open_cb, cli_context); 863866f15b3SPaul Luse break; 864866f15b3SPaul Luse case CLI_FILL: 865d52dbda2SJim Harris spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 866866f15b3SPaul Luse fill_blob_cb, cli_context); 867866f15b3SPaul Luse break; 868ff4fd58bSMike Gerdts case CLI_RECOVER: 869ff4fd58bSMike Gerdts unload_bs(cli_context, "", 0); 870ff4fd58bSMike Gerdts break; 8713453391bSMike Gerdts case CLI_DELETE_BLOB: 8723453391bSMike Gerdts spdk_bs_delete_blob(cli_context->bs, cli_context->blobid, 8733453391bSMike Gerdts delete_blob_cb, cli_context); 8743453391bSMike Gerdts break; 875866f15b3SPaul Luse 876866f15b3SPaul Luse default: 877866f15b3SPaul Luse /* should never get here */ 87898a2ec3eSPaul Luse exit(-1); 879866f15b3SPaul Luse break; 880866f15b3SPaul Luse } 8818fbbfccdSPaul Luse } 8828fbbfccdSPaul Luse 883c8658397SShuhei Matsumoto static void 884c8658397SShuhei Matsumoto base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, 885c8658397SShuhei Matsumoto void *event_ctx) 886c8658397SShuhei Matsumoto { 887ab2aad85SMike Gerdts printf("Unsupported bdev event: type %d on bdev %s\n", type, spdk_bdev_get_name(bdev)); 888c8658397SShuhei Matsumoto } 889c8658397SShuhei Matsumoto 8908fbbfccdSPaul Luse /* 891866f15b3SPaul Luse * Load the blobstore. 8928fbbfccdSPaul Luse */ 8938fbbfccdSPaul Luse static void 8948fbbfccdSPaul Luse load_bs(struct cli_context_t *cli_context) 8958fbbfccdSPaul Luse { 8968fbbfccdSPaul Luse struct spdk_bs_dev *bs_dev = NULL; 897c8658397SShuhei Matsumoto int rc; 898ff4fd58bSMike Gerdts struct spdk_bs_opts opts = {}; 899ff4fd58bSMike Gerdts struct spdk_bs_opts *optsp = NULL; 9008fbbfccdSPaul Luse 901c8658397SShuhei Matsumoto rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, 902c8658397SShuhei Matsumoto NULL, &bs_dev); 903c8658397SShuhei Matsumoto if (rc != 0) { 904c8658397SShuhei Matsumoto printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 9058fbbfccdSPaul Luse spdk_app_stop(-1); 9068fbbfccdSPaul Luse return; 9078fbbfccdSPaul Luse } 9088fbbfccdSPaul Luse 909ff4fd58bSMike Gerdts if (cli_context->action == CLI_RECOVER) { 910ff4fd58bSMike Gerdts spdk_bs_opts_init(&opts, sizeof(opts)); 911ff4fd58bSMike Gerdts opts.force_recover = true; 912ff4fd58bSMike Gerdts optsp = &opts; 913ff4fd58bSMike Gerdts } 914ff4fd58bSMike Gerdts 915ff4fd58bSMike Gerdts spdk_bs_load(bs_dev, optsp, load_bs_cb, cli_context); 9168fbbfccdSPaul Luse } 9178fbbfccdSPaul Luse 9181906b075SShuhei Matsumoto static int 9191906b075SShuhei Matsumoto print_bdev(void *ctx, struct spdk_bdev *bdev) 9201906b075SShuhei Matsumoto { 9211906b075SShuhei Matsumoto uint32_t *count = ctx; 9221906b075SShuhei Matsumoto 9231906b075SShuhei Matsumoto (*count)++; 9241906b075SShuhei Matsumoto 9251906b075SShuhei Matsumoto printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev)); 9261906b075SShuhei Matsumoto printf("\tbdev Product Name: %s\n", 9271906b075SShuhei Matsumoto spdk_bdev_get_product_name(bdev)); 9281906b075SShuhei Matsumoto return 0; 9291906b075SShuhei Matsumoto } 9301906b075SShuhei Matsumoto 9318fbbfccdSPaul Luse /* 9328fbbfccdSPaul Luse * Lists all the blobs on this blobstore. 9338fbbfccdSPaul Luse */ 9348fbbfccdSPaul Luse static void 935c5e63c71SPaul Luse list_bdevs(struct cli_context_t *cli_context) 9368fbbfccdSPaul Luse { 9371906b075SShuhei Matsumoto uint32_t count = 0; 9388fbbfccdSPaul Luse 9398fbbfccdSPaul Luse printf("\nList bdevs:\n"); 9408fbbfccdSPaul Luse 9411906b075SShuhei Matsumoto spdk_for_each_bdev(&count, print_bdev); 9421906b075SShuhei Matsumoto 9431906b075SShuhei Matsumoto if (count == 0) { 9448fbbfccdSPaul Luse printf("Could not find a bdev\n"); 9458fbbfccdSPaul Luse } 9468fbbfccdSPaul Luse 9478fbbfccdSPaul Luse printf("\n"); 948c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD) { 9498fbbfccdSPaul Luse spdk_app_stop(0); 950c5e63c71SPaul Luse } else { 951c5e63c71SPaul Luse cli_context->action = CLI_NONE; 952deb8ee5cSBen Walker cli_start(cli_context); 953c5e63c71SPaul Luse } 9548fbbfccdSPaul Luse } 9558fbbfccdSPaul Luse 9568fbbfccdSPaul Luse /* 9578fbbfccdSPaul Luse * Callback function for initializing a blob. 9588fbbfccdSPaul Luse */ 9598fbbfccdSPaul Luse static void 960866f15b3SPaul Luse bs_init_cb(void *cb_arg, struct spdk_blob_store *bs, 9618fbbfccdSPaul Luse int bserrno) 9628fbbfccdSPaul Luse { 9638fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 9648fbbfccdSPaul Luse 9658fbbfccdSPaul Luse if (bserrno) { 9668fbbfccdSPaul Luse unload_bs(cli_context, "Error in bs init callback", 9678fbbfccdSPaul Luse bserrno); 9688fbbfccdSPaul Luse return; 9698fbbfccdSPaul Luse } 9708fbbfccdSPaul Luse cli_context->bs = bs; 9718fbbfccdSPaul Luse printf("blobstore init'd: (%p)\n", cli_context->bs); 9728fbbfccdSPaul Luse 9738fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 9748fbbfccdSPaul Luse } 9758fbbfccdSPaul Luse 9768fbbfccdSPaul Luse /* 9778fbbfccdSPaul Luse * Initialize a new blobstore. 9788fbbfccdSPaul Luse */ 9798fbbfccdSPaul Luse static void 9808fbbfccdSPaul Luse init_bs(struct cli_context_t *cli_context) 9818fbbfccdSPaul Luse { 982c8658397SShuhei Matsumoto int rc; 9838fbbfccdSPaul Luse 984c8658397SShuhei Matsumoto printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name); 9858fbbfccdSPaul Luse 986c8658397SShuhei Matsumoto rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL, 987c8658397SShuhei Matsumoto &cli_context->bs_dev); 988c8658397SShuhei Matsumoto if (rc != 0) { 989c8658397SShuhei Matsumoto printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 9908fbbfccdSPaul Luse spdk_app_stop(-1); 9918fbbfccdSPaul Luse return; 9928fbbfccdSPaul Luse } 9938fbbfccdSPaul Luse 994866f15b3SPaul Luse spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb, 9958fbbfccdSPaul Luse cli_context); 9968fbbfccdSPaul Luse } 9978fbbfccdSPaul Luse 998d1d22046SJim Harris static void 999d1d22046SJim Harris spdk_bsdump_done(void *arg, int bserrno) 1000d1d22046SJim Harris { 1001d1d22046SJim Harris struct cli_context_t *cli_context = arg; 1002d1d22046SJim Harris 1003d1d22046SJim Harris if (cli_context->cli_mode == CLI_MODE_CMD) { 1004d1d22046SJim Harris spdk_app_stop(0); 1005d1d22046SJim Harris } else { 1006d1d22046SJim Harris cli_context->action = CLI_NONE; 1007deb8ee5cSBen Walker cli_start(cli_context); 1008d1d22046SJim Harris } 1009d1d22046SJim Harris } 1010d1d22046SJim Harris 1011d1d22046SJim Harris static void 1012d1d22046SJim Harris bsdump_print_xattr(FILE *fp, const char *bstype, const char *name, const void *value, 1013d1d22046SJim Harris size_t value_len) 1014d1d22046SJim Harris { 1015d1d22046SJim Harris if (strncmp(bstype, "BLOBFS", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) { 1016d1d22046SJim Harris if (strcmp(name, "name") == 0) { 1017d1d22046SJim Harris fprintf(fp, "%.*s", (int)value_len, (char *)value); 1018d1d22046SJim Harris } else if (strcmp(name, "length") == 0 && value_len == sizeof(uint64_t)) { 1019d1d22046SJim Harris uint64_t length; 1020d1d22046SJim Harris 1021d1d22046SJim Harris memcpy(&length, value, sizeof(length)); 1022d1d22046SJim Harris fprintf(fp, "%" PRIu64, length); 1023d1d22046SJim Harris } else { 1024d1d22046SJim Harris fprintf(fp, "?"); 1025d1d22046SJim Harris } 1026d1d22046SJim Harris } else if (strncmp(bstype, "LVOLSTORE", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) { 1027d1d22046SJim Harris if (strcmp(name, "name") == 0) { 1028d1d22046SJim Harris fprintf(fp, "%s", (char *)value); 1029643385eaSMike Gerdts } else if (strcmp(name, "uuid") == 0) { 1030643385eaSMike Gerdts struct spdk_uuid uuid; 1031d1d22046SJim Harris 1032643385eaSMike Gerdts if (spdk_uuid_parse(&uuid, (const char *)value) == 0) { 1033643385eaSMike Gerdts fprintf(fp, "%s", (const char *)value); 1034643385eaSMike Gerdts } else { 1035643385eaSMike Gerdts fprintf(fp, "? Invalid UUID"); 1036643385eaSMike Gerdts } 1037d1d22046SJim Harris } else { 1038d1d22046SJim Harris fprintf(fp, "?"); 1039d1d22046SJim Harris } 1040d1d22046SJim Harris } else { 1041d1d22046SJim Harris fprintf(fp, "?"); 1042d1d22046SJim Harris } 1043d1d22046SJim Harris } 1044d1d22046SJim Harris 1045d1d22046SJim Harris /* 1046d1d22046SJim Harris * Dump metadata of an existing blobstore in a human-readable format. 1047d1d22046SJim Harris */ 1048d1d22046SJim Harris static void 1049d1d22046SJim Harris dump_bs(struct cli_context_t *cli_context) 1050d1d22046SJim Harris { 1051c8658397SShuhei Matsumoto int rc; 1052d1d22046SJim Harris 1053c8658397SShuhei Matsumoto printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name); 1054d1d22046SJim Harris 1055c8658397SShuhei Matsumoto rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL, 1056c8658397SShuhei Matsumoto &cli_context->bs_dev); 1057c8658397SShuhei Matsumoto if (rc != 0) { 1058c8658397SShuhei Matsumoto printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 1059d1d22046SJim Harris spdk_app_stop(-1); 1060d1d22046SJim Harris return; 1061d1d22046SJim Harris } 1062d1d22046SJim Harris 1063d1d22046SJim Harris spdk_bs_dump(cli_context->bs_dev, stdout, bsdump_print_xattr, spdk_bsdump_done, cli_context); 1064d1d22046SJim Harris } 1065d1d22046SJim Harris 10668fbbfccdSPaul Luse /* 1067c5e63c71SPaul Luse * Common cmd/option parser for command and shell modes. 1068c5e63c71SPaul Luse */ 1069c5e63c71SPaul Luse static bool 1070c5e63c71SPaul Luse cmd_parser(int argc, char **argv, struct cli_context_t *cli_context) 10718fbbfccdSPaul Luse { 10728fbbfccdSPaul Luse int op; 1073c5e63c71SPaul Luse int cmd_chosen = 0; 1074c5e63c71SPaul Luse char resp; 10758fbbfccdSPaul Luse 10763453391bSMike Gerdts while ((op = getopt(argc, argv, "b:d:f:hij:l:m:n:p:r:s:w:DRST:Xx:")) != -1) { 10778fbbfccdSPaul Luse switch (op) { 1078cfa4cae0SPaul Luse case 'b': 1079cfa4cae0SPaul Luse if (strcmp(cli_context->bdev_name, "") == 0) { 1080cfa4cae0SPaul Luse snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg); 1081cfa4cae0SPaul Luse } else { 1082cfa4cae0SPaul Luse printf("Current setting for -b is: %s\n", cli_context->bdev_name); 1083cfa4cae0SPaul Luse usage(cli_context, "ERROR: -b option can only be set once.\n"); 1084cfa4cae0SPaul Luse } 1085cfa4cae0SPaul Luse break; 1086d1d22046SJim Harris case 'D': 1087d1d22046SJim Harris cmd_chosen++; 1088d1d22046SJim Harris cli_context->action = CLI_DUMP_BS; 1089d1d22046SJim Harris break; 10908fbbfccdSPaul Luse case 'd': 1091cf0d7736SPaul Luse if (argv[optind] != NULL) { 1092c5e63c71SPaul Luse cmd_chosen++; 10934952f254SDaniel Verkamp cli_context->action = CLI_DUMP_BLOB; 109476a577b0SMike Gerdts cli_context->blobid = spdk_strtoll(optarg, 0); 1095e5044f79SPaul Luse snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1096cf0d7736SPaul Luse } else { 1097cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 1098cf0d7736SPaul Luse } 10998fbbfccdSPaul Luse break; 11008fbbfccdSPaul Luse case 'f': 1101cf0d7736SPaul Luse if (argv[optind] != NULL) { 1102c5e63c71SPaul Luse cmd_chosen++; 11038fbbfccdSPaul Luse cli_context->action = CLI_FILL; 110476a577b0SMike Gerdts cli_context->blobid = spdk_strtoll(optarg, 0); 110576a577b0SMike Gerdts cli_context->fill_value = spdk_strtol(argv[optind], 0); 1106cf0d7736SPaul Luse } else { 1107cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 1108cf0d7736SPaul Luse } 11098fbbfccdSPaul Luse break; 1110c5e63c71SPaul Luse case 'h': 1111c5e63c71SPaul Luse cmd_chosen++; 1112c5e63c71SPaul Luse cli_context->action = CLI_HELP; 1113c5e63c71SPaul Luse break; 11148fbbfccdSPaul Luse case 'i': 1115e5044f79SPaul Luse if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 1116e5044f79SPaul Luse printf("Your entire blobstore will be destroyed. Are you sure? (y/n) "); 1117c5e63c71SPaul Luse if (scanf("%c%*c", &resp)) { 1118c5e63c71SPaul Luse if (resp == 'y' || resp == 'Y') { 1119c5e63c71SPaul Luse cmd_chosen++; 11208fbbfccdSPaul Luse cli_context->action = CLI_INIT_BS; 1121c5e63c71SPaul Luse } else { 1122c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD) { 1123e5044f79SPaul Luse spdk_app_stop(0); 1124e5044f79SPaul Luse return false; 1125c5e63c71SPaul Luse } 1126c5e63c71SPaul Luse } 1127c5e63c71SPaul Luse } 1128e5044f79SPaul Luse } else { 1129e5044f79SPaul Luse cmd_chosen++; 1130e5044f79SPaul Luse cli_context->action = CLI_INIT_BS; 1131e5044f79SPaul Luse } 11328fbbfccdSPaul Luse break; 11336a41e84cSWANGHAILIANG case 'j': 11346a41e84cSWANGHAILIANG if (cli_context->app_started == false) { 11356a41e84cSWANGHAILIANG cli_context->config_file = optarg; 11366a41e84cSWANGHAILIANG } else { 11376a41e84cSWANGHAILIANG usage(cli_context, "ERROR: -j option not valid during shell mode.\n"); 11386a41e84cSWANGHAILIANG } 11396a41e84cSWANGHAILIANG break; 11408fbbfccdSPaul Luse case 'r': 1141cf0d7736SPaul Luse if (argv[optind] != NULL) { 1142c5e63c71SPaul Luse cmd_chosen++; 11438fbbfccdSPaul Luse cli_context->action = CLI_REM_XATTR; 114476a577b0SMike Gerdts cli_context->blobid = spdk_strtoll(optarg, 0); 1145e5044f79SPaul Luse snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1146cf0d7736SPaul Luse } else { 1147cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 1148cf0d7736SPaul Luse } 11498fbbfccdSPaul Luse break; 11508fbbfccdSPaul Luse case 'l': 11518fbbfccdSPaul Luse if (strcmp("bdevs", optarg) == 0) { 1152c5e63c71SPaul Luse cmd_chosen++; 11538fbbfccdSPaul Luse cli_context->action = CLI_LIST_BDEVS; 11548fbbfccdSPaul Luse } else if (strcmp("blobs", optarg) == 0) { 1155c5e63c71SPaul Luse cmd_chosen++; 11568fbbfccdSPaul Luse cli_context->action = CLI_LIST_BLOBS; 11578fbbfccdSPaul Luse } else { 1158e5044f79SPaul Luse usage(cli_context, "ERROR: invalid option for list\n"); 11598fbbfccdSPaul Luse } 11608fbbfccdSPaul Luse break; 11618fbbfccdSPaul Luse case 'm': 1162cf0d7736SPaul Luse if (argv[optind] != NULL) { 1163c5e63c71SPaul Luse cmd_chosen++; 11644952f254SDaniel Verkamp cli_context->action = CLI_IMPORT_BLOB; 116576a577b0SMike Gerdts cli_context->blobid = spdk_strtoll(optarg, 0); 1166e5044f79SPaul Luse snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1167cf0d7736SPaul Luse } else { 1168cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 1169cf0d7736SPaul Luse } 11708fbbfccdSPaul Luse break; 11718fbbfccdSPaul Luse case 'n': 1172404c5a3fSShuhei Matsumoto cli_context->num_clusters = spdk_strtol(optarg, 10); 11738fbbfccdSPaul Luse if (cli_context->num_clusters > 0) { 1174cf0d7736SPaul Luse cmd_chosen++; 11758fbbfccdSPaul Luse cli_context->action = CLI_CREATE_BLOB; 11768fbbfccdSPaul Luse } else { 1177e5044f79SPaul Luse usage(cli_context, "ERROR: invalid option for new\n"); 11788fbbfccdSPaul Luse } 11798fbbfccdSPaul Luse break; 11808fbbfccdSPaul Luse case 'p': 1181c5e63c71SPaul Luse cmd_chosen++; 11828fbbfccdSPaul Luse cli_context->action = CLI_SET_SUPER; 118376a577b0SMike Gerdts cli_context->superid = spdk_strtoll(optarg, 0); 11848fbbfccdSPaul Luse break; 1185ff4fd58bSMike Gerdts case 'R': 1186ff4fd58bSMike Gerdts cmd_chosen++; 1187ff4fd58bSMike Gerdts cli_context->action = CLI_RECOVER; 1188ff4fd58bSMike Gerdts break; 1189c5e63c71SPaul Luse case 'S': 1190c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD) { 1191cf0d7736SPaul Luse cmd_chosen++; 1192c5e63c71SPaul Luse cli_context->cli_mode = CLI_MODE_SHELL; 1193c5e63c71SPaul Luse } 1194c5e63c71SPaul Luse cli_context->action = CLI_NONE; 1195c5e63c71SPaul Luse break; 11968fbbfccdSPaul Luse case 's': 1197c5e63c71SPaul Luse cmd_chosen++; 11988fbbfccdSPaul Luse if (strcmp("bs", optarg) == 0) { 11998fbbfccdSPaul Luse cli_context->action = CLI_SHOW_BS; 12008fbbfccdSPaul Luse } else { 12018fbbfccdSPaul Luse cli_context->action = CLI_SHOW_BLOB; 120276a577b0SMike Gerdts cli_context->blobid = spdk_strtoll(optarg, 0); 12038fbbfccdSPaul Luse } 12048fbbfccdSPaul Luse break; 1205e5044f79SPaul Luse case 'T': 1206e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD) { 1207e5044f79SPaul Luse cmd_chosen++; 1208e5044f79SPaul Luse cli_context->cli_mode = CLI_MODE_SCRIPT; 1209e5044f79SPaul Luse if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) { 1210e5044f79SPaul Luse g_script.ignore_errors = true; 1211e5044f79SPaul Luse } else { 1212e5044f79SPaul Luse g_script.ignore_errors = false; 1213e5044f79SPaul Luse } 1214e5044f79SPaul Luse snprintf(cli_context->script_file, BUFSIZE, "%s", optarg); 1215e5044f79SPaul Luse } else { 1216e5044f79SPaul Luse cli_context->action = CLI_NONE; 1217e5044f79SPaul Luse } 1218e5044f79SPaul Luse break; 12193453391bSMike Gerdts case 'w': 12203453391bSMike Gerdts cmd_chosen++; 12213453391bSMike Gerdts cli_context->action = CLI_DELETE_BLOB; 12223453391bSMike Gerdts cli_context->blobid = spdk_strtoll(optarg, 0); 12233453391bSMike Gerdts break; 1224c5e63c71SPaul Luse case 'X': 1225c5e63c71SPaul Luse cmd_chosen++; 1226c5e63c71SPaul Luse cli_context->action = CLI_SHELL_EXIT; 1227c5e63c71SPaul Luse break; 12288fbbfccdSPaul Luse case 'x': 1229cf0d7736SPaul Luse if (argv[optind] != NULL || argv[optind + 1] != NULL) { 1230c5e63c71SPaul Luse cmd_chosen++; 12318fbbfccdSPaul Luse cli_context->action = CLI_SET_XATTR; 123276a577b0SMike Gerdts cli_context->blobid = spdk_strtoll(optarg, 0); 1233e5044f79SPaul Luse snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1234e5044f79SPaul Luse snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]); 1235cf0d7736SPaul Luse } else { 1236cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 1237cf0d7736SPaul Luse } 12388fbbfccdSPaul Luse break; 12398fbbfccdSPaul Luse default: 1240e5044f79SPaul Luse usage(cli_context, "ERROR: invalid option\n"); 12418fbbfccdSPaul Luse } 1242db26b96fSPaul Luse /* only one actual command can be done at a time */ 1243db26b96fSPaul Luse if (cmd_chosen > 1) { 1244e5044f79SPaul Luse usage(cli_context, "Error: Please choose only one command\n"); 12458fbbfccdSPaul Luse } 12468fbbfccdSPaul Luse } 12478fbbfccdSPaul Luse 1248c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) { 1249e5044f79SPaul Luse usage(cli_context, "Error: Please choose a command.\n"); 12508fbbfccdSPaul Luse } 12518fbbfccdSPaul Luse 1252cfa4cae0SPaul Luse /* 1253cfa4cae0SPaul Luse * We don't check the local boolean because in some modes it will have been set 1254cfa4cae0SPaul Luse * on and earlier command. 1255cfa4cae0SPaul Luse */ 1256df5ad409Swanghailiangx if ((strcmp(cli_context->bdev_name, "") == 0) && (cli_context->action != CLI_HELP)) { 1257cfa4cae0SPaul Luse usage(cli_context, "Error: -b option is required.\n"); 1258cfa4cae0SPaul Luse cmd_chosen = 0; 1259cfa4cae0SPaul Luse } 1260cfa4cae0SPaul Luse 1261c5e63c71SPaul Luse /* in shell mode we'll call getopt multiple times so need to reset its index */ 1262c5e63c71SPaul Luse optind = 0; 1263db26b96fSPaul Luse return (cmd_chosen == 1); 1264c5e63c71SPaul Luse } 1265c5e63c71SPaul Luse 1266c5e63c71SPaul Luse /* 1267e5044f79SPaul Luse * In script mode, we parsed a script file at startup and saved off a bunch of cmd 1268e5044f79SPaul Luse * lines that we now parse with each run of cli_start so we us the same cmd parser 1269e5044f79SPaul Luse * as cmd and shell modes. 1270e5044f79SPaul Luse */ 1271e5044f79SPaul Luse static bool 1272e5044f79SPaul Luse line_parser(struct cli_context_t *cli_context) 1273e5044f79SPaul Luse { 1274e5044f79SPaul Luse bool cmd_chosen; 1275e5044f79SPaul Luse char *tok = NULL; 1276*b37db069SXuQi char *sp = NULL; 1277e5044f79SPaul Luse int blob_num = 0; 1278e5044f79SPaul Luse int start_idx = cli_context->argc; 1279e5044f79SPaul Luse int i; 1280e5044f79SPaul Luse 1281e5044f79SPaul Luse printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]); 1282*b37db069SXuQi tok = strtok_r(g_script.cmdline[g_script.cmdline_idx], " ", &sp); 1283e5044f79SPaul Luse while (tok != NULL) { 1284e5044f79SPaul Luse /* 1285e5044f79SPaul Luse * We support one replaceable token right now, a $Bn 1286e5044f79SPaul Luse * represents the blobid that was created in position n 1287e5044f79SPaul Luse * so fish this out now and use it here. 1288e5044f79SPaul Luse */ 1289e5044f79SPaul Luse cli_context->argv[cli_context->argc] = strdup(tok); 1290e5044f79SPaul Luse if (tok[0] == '$' && tok[1] == 'B') { 1291e5044f79SPaul Luse tok += 2; 1292404c5a3fSShuhei Matsumoto blob_num = spdk_strtol(tok, 10); 12935589fc4aSPaul Luse if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) { 1294e5044f79SPaul Luse cli_context->argv[cli_context->argc] = 1295e5044f79SPaul Luse realloc(cli_context->argv[cli_context->argc], BUFSIZE); 1296e5044f79SPaul Luse if (cli_context->argv[cli_context->argc] == NULL) { 1297e5044f79SPaul Luse printf("ERROR: unable to realloc memory\n"); 1298e5044f79SPaul Luse spdk_app_stop(-1); 1299e5044f79SPaul Luse } 1300e5044f79SPaul Luse if (g_script.blobid[blob_num] == 0) { 1301e5044f79SPaul Luse printf("ERROR: There is no blob for $B%d\n", 1302e5044f79SPaul Luse blob_num); 1303e5044f79SPaul Luse } 1304e5044f79SPaul Luse snprintf(cli_context->argv[cli_context->argc], BUFSIZE, 1305e5044f79SPaul Luse "%" PRIu64, g_script.blobid[blob_num]); 13065589fc4aSPaul Luse } else { 13075589fc4aSPaul Luse printf("ERROR: Invalid token or exceeded max blobs of %d\n", 13085589fc4aSPaul Luse MAX_SCRIPT_BLOBS); 13095589fc4aSPaul Luse } 1310e5044f79SPaul Luse } 1311e5044f79SPaul Luse cli_context->argc++; 1312*b37db069SXuQi tok = strtok_r(NULL, " ", &sp); 1313e5044f79SPaul Luse } 1314e5044f79SPaul Luse 1315e5044f79SPaul Luse /* call parse cmd line with user input as args */ 1316e5044f79SPaul Luse cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1317e5044f79SPaul Luse 1318e5044f79SPaul Luse /* free strdup memory and reset arg count for next shell interaction */ 1319e5044f79SPaul Luse for (i = start_idx; i < cli_context->argc; i++) { 1320e5044f79SPaul Luse free(cli_context->argv[i]); 1321e5044f79SPaul Luse cli_context->argv[i] = NULL; 1322e5044f79SPaul Luse } 1323e5044f79SPaul Luse cli_context->argc = 1; 1324e5044f79SPaul Luse 1325e5044f79SPaul Luse g_script.cmdline_idx++; 1326e5044f79SPaul Luse assert(g_script.cmdline_idx < MAX_SCRIPT_LINES); 1327e5044f79SPaul Luse 1328e5044f79SPaul Luse if (cmd_chosen == false) { 1329e5044f79SPaul Luse printf("ERROR: Invalid script line starting with: %s\n\n", 1330e5044f79SPaul Luse g_script.cmdline[g_script.cmdline_idx - 1]); 1331e5044f79SPaul Luse if (g_script.ignore_errors == false) { 1332e5044f79SPaul Luse printf("** Aborting **\n"); 1333e5044f79SPaul Luse cli_context->action = CLI_SHELL_EXIT; 1334e5044f79SPaul Luse cmd_chosen = true; 1335e5044f79SPaul Luse unload_bs(cli_context, "", 0); 1336e5044f79SPaul Luse } else { 1337e5044f79SPaul Luse printf("** Skipping **\n"); 1338e5044f79SPaul Luse } 1339e5044f79SPaul Luse } 1340e5044f79SPaul Luse 1341e5044f79SPaul Luse return cmd_chosen; 1342e5044f79SPaul Luse } 1343e5044f79SPaul Luse 1344e5044f79SPaul Luse /* 1345e5044f79SPaul Luse * For script mode, we read a series of commands from a text file and store them 1346e5044f79SPaul Luse * in a global struct. That, along with the cli_mode that tells us we're in 1347e5044f79SPaul Luse * script mode is what feeds the rest of the app in the same way as is it were 1348e5044f79SPaul Luse * getting commands from shell mode. 1349e5044f79SPaul Luse */ 1350e5044f79SPaul Luse static void 1351e5044f79SPaul Luse parse_script(struct cli_context_t *cli_context) 1352e5044f79SPaul Luse { 1353e5044f79SPaul Luse FILE *fp = NULL; 1354e5044f79SPaul Luse size_t bufsize = BUFSIZE; 1355e5044f79SPaul Luse int64_t bytes_in = 0; 1356e5044f79SPaul Luse int i = 0; 1357e5044f79SPaul Luse 1358e5044f79SPaul Luse /* initialize global script values */ 1359e5044f79SPaul Luse for (i = 0; i < MAX_SCRIPT_BLOBS; i++) { 1360e5044f79SPaul Luse g_script.blobid[i] = 0; 1361e5044f79SPaul Luse } 1362e5044f79SPaul Luse g_script.blobid_idx = 0; 1363e5044f79SPaul Luse g_script.cmdline_idx = 0; 1364e5044f79SPaul Luse i = 0; 1365e5044f79SPaul Luse 1366e5044f79SPaul Luse fp = fopen(cli_context->script_file, "r"); 1367e5044f79SPaul Luse if (fp == NULL) { 1368e5044f79SPaul Luse printf("ERROR: unable to open script: %s\n", 1369e5044f79SPaul Luse cli_context->script_file); 1370e5044f79SPaul Luse cli_cleanup(cli_context); 1371e5044f79SPaul Luse exit(-1); 1372e5044f79SPaul Luse } 1373e5044f79SPaul Luse 1374e5044f79SPaul Luse do { 1375e5044f79SPaul Luse bytes_in = getline(&g_script.cmdline[i], &bufsize, fp); 1376e5044f79SPaul Luse if (bytes_in > 0) { 1377e5044f79SPaul Luse /* replace newline with null */ 1378e5044f79SPaul Luse spdk_str_chomp(g_script.cmdline[i]); 1379e5044f79SPaul Luse 1380e5044f79SPaul Luse /* ignore comments */ 1381e5044f79SPaul Luse if (g_script.cmdline[i][0] != '#') { 1382e5044f79SPaul Luse i++; 1383e5044f79SPaul Luse } 1384e5044f79SPaul Luse } 13855589fc4aSPaul Luse } while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1); 1386e5044f79SPaul Luse fclose(fp); 1387e5044f79SPaul Luse 1388e5044f79SPaul Luse /* add an exit cmd in case they didn't */ 1389e5044f79SPaul Luse g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE); 1390e5044f79SPaul Luse if (g_script.cmdline[i] == NULL) { 1391e5044f79SPaul Luse int j; 1392e5044f79SPaul Luse 1393e5044f79SPaul Luse for (j = 0; j < i; j++) { 1394e5044f79SPaul Luse free(g_script.cmdline[j]); 1395e5044f79SPaul Luse g_script.cmdline[j] = NULL; 1396e5044f79SPaul Luse } 1397e5044f79SPaul Luse unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0); 1398e5044f79SPaul Luse } 1399e5044f79SPaul Luse snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X"); 1400e5044f79SPaul Luse g_script.max_index = i; 1401e5044f79SPaul Luse } 1402e5044f79SPaul Luse 1403e5044f79SPaul Luse /* 1404c5e63c71SPaul Luse * Provides for a shell interface as opposed to one shot command line. 1405c5e63c71SPaul Luse */ 1406c5e63c71SPaul Luse static bool 1407c5e63c71SPaul Luse cli_shell(void *arg1, void *arg2) 1408c5e63c71SPaul Luse { 1409c5e63c71SPaul Luse struct cli_context_t *cli_context = arg1; 1410c5e63c71SPaul Luse char *line = NULL; 1411c5e63c71SPaul Luse ssize_t buf_size = 0; 1412c5e63c71SPaul Luse ssize_t bytes_in = 0; 1413c5e63c71SPaul Luse ssize_t tok_len = 0; 1414c5e63c71SPaul Luse char *tok = NULL; 1415*b37db069SXuQi char *sp = NULL; 1416c5e63c71SPaul Luse bool cmd_chosen = false; 1417c5e63c71SPaul Luse int start_idx = cli_context->argc; 1418c5e63c71SPaul Luse int i; 1419c5e63c71SPaul Luse 1420c5e63c71SPaul Luse printf("blob> "); 1421c5e63c71SPaul Luse bytes_in = getline(&line, &buf_size, stdin); 1422c5e63c71SPaul Luse 1423168bc9f1SDaniel Verkamp /* If getline() failed (EOF), exit the shell. */ 1424168bc9f1SDaniel Verkamp if (bytes_in < 0) { 1425168bc9f1SDaniel Verkamp free(line); 1426168bc9f1SDaniel Verkamp cli_context->action = CLI_SHELL_EXIT; 1427168bc9f1SDaniel Verkamp return true; 1428168bc9f1SDaniel Verkamp } 1429168bc9f1SDaniel Verkamp 1430c5e63c71SPaul Luse /* parse input and update cli_context so we can use common option parser */ 1431c5e63c71SPaul Luse if (bytes_in > 0) { 1432*b37db069SXuQi tok = strtok_r(line, " ", &sp); 1433c5e63c71SPaul Luse } 1434c5e63c71SPaul Luse while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) { 1435c5e63c71SPaul Luse cli_context->argv[cli_context->argc] = strdup(tok); 1436c5e63c71SPaul Luse tok_len = strlen(tok); 1437c5e63c71SPaul Luse cli_context->argc++; 1438*b37db069SXuQi tok = strtok_r(NULL, " ", &sp); 1439c5e63c71SPaul Luse } 1440c5e63c71SPaul Luse 1441c5e63c71SPaul Luse /* replace newline on last arg with null */ 1442c5e63c71SPaul Luse if (tok_len) { 1443c5e63c71SPaul Luse spdk_str_chomp(cli_context->argv[cli_context->argc - 1]); 1444c5e63c71SPaul Luse } 1445c5e63c71SPaul Luse 1446c5e63c71SPaul Luse /* call parse cmd line with user input as args */ 1447c5e63c71SPaul Luse cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1448c5e63c71SPaul Luse 1449c5e63c71SPaul Luse /* free strdup mem & reset arg count for next shell interaction */ 1450c5e63c71SPaul Luse for (i = start_idx; i < cli_context->argc; i++) { 1451c5e63c71SPaul Luse free(cli_context->argv[i]); 1452d4e565b4SPaul Luse cli_context->argv[i] = NULL; 1453c5e63c71SPaul Luse } 1454c5e63c71SPaul Luse cli_context->argc = 1; 1455c5e63c71SPaul Luse 1456c5e63c71SPaul Luse free(line); 1457c5e63c71SPaul Luse 1458c5e63c71SPaul Luse return cmd_chosen; 1459c5e63c71SPaul Luse } 1460c5e63c71SPaul Luse 1461866f15b3SPaul Luse /* 1462866f15b3SPaul Luse * This is the function we pass into the SPDK framework that gets 1463866f15b3SPaul Luse * called first. 1464866f15b3SPaul Luse */ 1465866f15b3SPaul Luse static void 1466deb8ee5cSBen Walker cli_start(void *arg1) 1467866f15b3SPaul Luse { 1468866f15b3SPaul Luse struct cli_context_t *cli_context = arg1; 1469866f15b3SPaul Luse 1470866f15b3SPaul Luse /* 1471e5044f79SPaul Luse * If we're in script mode, we already have a list of commands so 1472e5044f79SPaul Luse * just need to pull them out one at a time and process them. 1473e5044f79SPaul Luse */ 1474e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1475e5044f79SPaul Luse while (line_parser(cli_context) == false); 1476e5044f79SPaul Luse } 1477e5044f79SPaul Luse 1478e5044f79SPaul Luse /* 1479866f15b3SPaul Luse * The initial cmd line options are parsed once before this function is 1480866f15b3SPaul Luse * called so if there is no action, we're in shell mode and will loop 1481866f15b3SPaul Luse * here until a a valid option is parsed and returned. 1482866f15b3SPaul Luse */ 1483866f15b3SPaul Luse if (cli_context->action == CLI_NONE) { 1484866f15b3SPaul Luse while (cli_shell(cli_context, NULL) == false); 1485866f15b3SPaul Luse } 1486866f15b3SPaul Luse 1487866f15b3SPaul Luse /* Decide what to do next based on cmd line parsing. */ 1488866f15b3SPaul Luse switch (cli_context->action) { 1489866f15b3SPaul Luse case CLI_SET_SUPER: 1490866f15b3SPaul Luse case CLI_SHOW_BS: 1491866f15b3SPaul Luse case CLI_CREATE_BLOB: 1492866f15b3SPaul Luse case CLI_SET_XATTR: 1493866f15b3SPaul Luse case CLI_REM_XATTR: 1494866f15b3SPaul Luse case CLI_SHOW_BLOB: 1495866f15b3SPaul Luse case CLI_LIST_BLOBS: 14964952f254SDaniel Verkamp case CLI_DUMP_BLOB: 14974952f254SDaniel Verkamp case CLI_IMPORT_BLOB: 1498866f15b3SPaul Luse case CLI_FILL: 1499ff4fd58bSMike Gerdts case CLI_RECOVER: 15003453391bSMike Gerdts case CLI_DELETE_BLOB: 1501866f15b3SPaul Luse load_bs(cli_context); 1502866f15b3SPaul Luse break; 1503866f15b3SPaul Luse case CLI_INIT_BS: 1504866f15b3SPaul Luse init_bs(cli_context); 1505866f15b3SPaul Luse break; 1506d1d22046SJim Harris case CLI_DUMP_BS: 1507d1d22046SJim Harris dump_bs(cli_context); 1508d1d22046SJim Harris break; 1509866f15b3SPaul Luse case CLI_LIST_BDEVS: 1510866f15b3SPaul Luse list_bdevs(cli_context); 1511866f15b3SPaul Luse break; 1512866f15b3SPaul Luse case CLI_SHELL_EXIT: 1513866f15b3SPaul Luse /* 1514866f15b3SPaul Luse * Because shell mode reuses cmd mode functions, the blobstore 1515866f15b3SPaul Luse * is loaded/unloaded with every action so we just need to 1516866f15b3SPaul Luse * stop the framework. For this app there's no need to optimize 1517866f15b3SPaul Luse * and keep the blobstore open while the app is in shell mode. 1518866f15b3SPaul Luse */ 1519866f15b3SPaul Luse spdk_app_stop(0); 1520866f15b3SPaul Luse break; 1521866f15b3SPaul Luse case CLI_HELP: 1522e5044f79SPaul Luse usage(cli_context, ""); 1523866f15b3SPaul Luse unload_complete(cli_context, 0); 1524866f15b3SPaul Luse break; 1525866f15b3SPaul Luse default: 1526866f15b3SPaul Luse /* should never get here */ 152798a2ec3eSPaul Luse exit(-1); 1528866f15b3SPaul Luse break; 1529866f15b3SPaul Luse } 1530866f15b3SPaul Luse } 1531c5e63c71SPaul Luse 1532c5e63c71SPaul Luse int 1533c5e63c71SPaul Luse main(int argc, char **argv) 1534c5e63c71SPaul Luse { 1535c5e63c71SPaul Luse struct spdk_app_opts opts = {}; 1536c5e63c71SPaul Luse struct cli_context_t *cli_context = NULL; 1537e5044f79SPaul Luse bool cmd_chosen; 1538c5e63c71SPaul Luse int rc = 0; 1539c5e63c71SPaul Luse 1540c5e63c71SPaul Luse if (argc < 2) { 1541e5044f79SPaul Luse usage(cli_context, "ERROR: Invalid option\n"); 1542e5044f79SPaul Luse exit(-1); 1543c5e63c71SPaul Luse } 1544c5e63c71SPaul Luse 1545c5e63c71SPaul Luse cli_context = calloc(1, sizeof(struct cli_context_t)); 1546c5e63c71SPaul Luse if (cli_context == NULL) { 1547c5e63c71SPaul Luse printf("ERROR: could not allocate context structure\n"); 1548c5e63c71SPaul Luse exit(-1); 1549c5e63c71SPaul Luse } 1550cfa4cae0SPaul Luse 1551c5e63c71SPaul Luse /* default to CMD mode until we've parsed the first parms */ 1552c5e63c71SPaul Luse cli_context->cli_mode = CLI_MODE_CMD; 1553c5e63c71SPaul Luse cli_context->argv[0] = strdup(argv[0]); 1554c5e63c71SPaul Luse cli_context->argc = 1; 1555c5e63c71SPaul Luse 1556c5e63c71SPaul Luse /* parse command line */ 1557e5044f79SPaul Luse cmd_chosen = cmd_parser(argc, argv, cli_context); 1558c5e63c71SPaul Luse free(cli_context->argv[0]); 1559e5044f79SPaul Luse cli_context->argv[0] = NULL; 1560e5044f79SPaul Luse if (cmd_chosen == false) { 1561e5044f79SPaul Luse cli_cleanup(cli_context); 1562e5044f79SPaul Luse exit(-1); 1563e5044f79SPaul Luse } 1564c5e63c71SPaul Luse 1565c5e63c71SPaul Luse /* after displaying help, just exit */ 1566c5e63c71SPaul Luse if (cli_context->action == CLI_HELP) { 1567e5044f79SPaul Luse usage(cli_context, ""); 1568c5e63c71SPaul Luse cli_cleanup(cli_context); 1569c5e63c71SPaul Luse exit(-1); 1570c5e63c71SPaul Luse } 1571c5e63c71SPaul Luse 1572c5e63c71SPaul Luse /* if they don't supply a conf name, use the default */ 1573c5e63c71SPaul Luse if (!cli_context->config_file) { 1574c5e63c71SPaul Luse cli_context->config_file = program_conf; 1575c5e63c71SPaul Luse } 1576c5e63c71SPaul Luse 1577c5e63c71SPaul Luse /* if the config file doesn't exist, tell them how to make one */ 1578c5e63c71SPaul Luse if (access(cli_context->config_file, F_OK) == -1) { 1579c5e63c71SPaul Luse printf("Error: No config file found.\n"); 15806a41e84cSWANGHAILIANG printf("To create a config file named 'blobcli.json' for your NVMe device:\n"); 15818c3e71f0STomasz Zawadzki printf(" <path to spdk>/scripts/gen_nvme.sh --json-with-subsystems > blobcli.json\n"); 1582c5e63c71SPaul Luse printf("and then re-run the cli tool.\n"); 158398a2ec3eSPaul Luse exit(-1); 1584c5e63c71SPaul Luse } 1585c5e63c71SPaul Luse 1586e5044f79SPaul Luse /* 1587e5044f79SPaul Luse * For script mode we keep a bunch of stuff in a global since 1588e5044f79SPaul Luse * none if it is passed back and forth to SPDK. 1589e5044f79SPaul Luse */ 1590e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1591e5044f79SPaul Luse /* 1592e5044f79SPaul Luse * Now we'll build up the global which will direct this run of the app 1593e5044f79SPaul Luse * as it will have a list (g_script) of all of the commands line by 1594e5044f79SPaul Luse * line as if they were typed in on the shell at cmd line. 1595e5044f79SPaul Luse */ 1596e5044f79SPaul Luse parse_script(cli_context); 1597e5044f79SPaul Luse } 1598e5044f79SPaul Luse 15998fbbfccdSPaul Luse /* Set default values in opts struct along with name and conf file. */ 160048701bd9SZiye Yang spdk_app_opts_init(&opts, sizeof(opts)); 16018fbbfccdSPaul Luse opts.name = "blobcli"; 16026a41e84cSWANGHAILIANG opts.json_config_file = cli_context->config_file; 16035db859daSKrzysztof Karas opts.rpc_addr = NULL; 16048fbbfccdSPaul Luse 1605c5e63c71SPaul Luse cli_context->app_started = true; 160636287957SBen Walker rc = spdk_app_start(&opts, cli_start, cli_context); 16078fbbfccdSPaul Luse if (rc) { 16088fbbfccdSPaul Luse printf("ERROR!\n"); 16098fbbfccdSPaul Luse } 16108fbbfccdSPaul Luse 16118fbbfccdSPaul Luse /* Free up memory that we allocated */ 16128fbbfccdSPaul Luse cli_cleanup(cli_context); 16138fbbfccdSPaul Luse 16148fbbfccdSPaul Luse /* Gracefully close out all of the SPDK subsystems. */ 16158fbbfccdSPaul Luse spdk_app_fini(); 16168fbbfccdSPaul Luse return rc; 16178fbbfccdSPaul Luse } 1618