18fbbfccdSPaul Luse /*- 28fbbfccdSPaul Luse * BSD LICENSE 38fbbfccdSPaul Luse * 48fbbfccdSPaul Luse * Copyright (c) Intel Corporation. 58fbbfccdSPaul Luse * All rights reserved. 68fbbfccdSPaul Luse * 78fbbfccdSPaul Luse * Redistribution and use in source and binary forms, with or without 88fbbfccdSPaul Luse * modification, are permitted provided that the following conditions 98fbbfccdSPaul Luse * are met: 108fbbfccdSPaul Luse * 118fbbfccdSPaul Luse * * Redistributions of source code must retain the above copyright 128fbbfccdSPaul Luse * notice, this list of conditions and the following disclaimer. 138fbbfccdSPaul Luse * * Redistributions in binary form must reproduce the above copyright 148fbbfccdSPaul Luse * notice, this list of conditions and the following disclaimer in 158fbbfccdSPaul Luse * the documentation and/or other materials provided with the 168fbbfccdSPaul Luse * distribution. 178fbbfccdSPaul Luse * * Neither the name of Intel Corporation nor the names of its 188fbbfccdSPaul Luse * contributors may be used to endorse or promote products derived 198fbbfccdSPaul Luse * from this software without specific prior written permission. 208fbbfccdSPaul Luse * 218fbbfccdSPaul Luse * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 228fbbfccdSPaul Luse * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 238fbbfccdSPaul Luse * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 248fbbfccdSPaul Luse * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 258fbbfccdSPaul Luse * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 268fbbfccdSPaul Luse * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 278fbbfccdSPaul Luse * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 288fbbfccdSPaul Luse * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 298fbbfccdSPaul Luse * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 308fbbfccdSPaul Luse * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 318fbbfccdSPaul Luse * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 328fbbfccdSPaul Luse */ 338fbbfccdSPaul Luse 348fbbfccdSPaul Luse #include "spdk/stdinc.h" 358fbbfccdSPaul Luse 368fbbfccdSPaul Luse #include "spdk/bdev.h" 378fbbfccdSPaul Luse #include "spdk/env.h" 388fbbfccdSPaul Luse #include "spdk/event.h" 398fbbfccdSPaul Luse #include "spdk/blob_bdev.h" 408fbbfccdSPaul Luse #include "spdk/blob.h" 418fbbfccdSPaul Luse #include "spdk/log.h" 428fbbfccdSPaul Luse #include "spdk/version.h" 43c5e63c71SPaul Luse #include "spdk/string.h" 448fbbfccdSPaul Luse 458fbbfccdSPaul Luse /* 46866f15b3SPaul Luse * The following is not a public header file, but the CLI does expose 478fbbfccdSPaul Luse * some internals of blobstore for dev/debug puposes so we 488fbbfccdSPaul Luse * include it here. 498fbbfccdSPaul Luse */ 508fbbfccdSPaul Luse #include "../lib/blob/blobstore.h" 51c5e63c71SPaul Luse static void cli_start(void *arg1, void *arg2); 528fbbfccdSPaul Luse 538fbbfccdSPaul Luse static const char *program_name = "blobcli"; 54866f15b3SPaul Luse /* default name for .conf file, any name can be used however with -c switch */ 558fbbfccdSPaul Luse static const char *program_conf = "blobcli.conf"; 568fbbfccdSPaul Luse 57c5e63c71SPaul Luse /* 58c5e63c71SPaul Luse * CMD mode runs one command at a time which can be annoying as the init takes 59c5e63c71SPaul Luse * a few seconds, so the shell mode, invoked with -S, does the init once and gives 60e5044f79SPaul Luse * the user an interactive shell instead. With script mode init is also done just 61e5044f79SPaul Luse * once. 62c5e63c71SPaul Luse */ 63c5e63c71SPaul Luse enum cli_mode_type { 64c5e63c71SPaul Luse CLI_MODE_CMD, 65e5044f79SPaul Luse CLI_MODE_SHELL, 66e5044f79SPaul Luse CLI_MODE_SCRIPT 67c5e63c71SPaul Luse }; 68c5e63c71SPaul Luse 698fbbfccdSPaul Luse enum cli_action_type { 70c5e63c71SPaul Luse CLI_NONE, 714952f254SDaniel Verkamp CLI_IMPORT_BLOB, 724952f254SDaniel Verkamp CLI_DUMP_BLOB, 738fbbfccdSPaul Luse CLI_FILL, 748fbbfccdSPaul Luse CLI_REM_XATTR, 758fbbfccdSPaul Luse CLI_SET_XATTR, 768fbbfccdSPaul Luse CLI_SET_SUPER, 778fbbfccdSPaul Luse CLI_SHOW_BS, 788fbbfccdSPaul Luse CLI_SHOW_BLOB, 798fbbfccdSPaul Luse CLI_CREATE_BLOB, 808fbbfccdSPaul Luse CLI_LIST_BDEVS, 818fbbfccdSPaul Luse CLI_LIST_BLOBS, 82c5e63c71SPaul Luse CLI_INIT_BS, 83c5e63c71SPaul Luse CLI_SHELL_EXIT, 84e5044f79SPaul Luse CLI_HELP, 858fbbfccdSPaul Luse }; 868fbbfccdSPaul Luse 87866f15b3SPaul Luse #define BUFSIZE 255 88e5044f79SPaul Luse #define MAX_ARGS 16 89866f15b3SPaul Luse #define ALIGN_4K 4096 90866f15b3SPaul Luse #define STARTING_PAGE 0 91866f15b3SPaul Luse #define NUM_PAGES 1 92e5044f79SPaul Luse 93c5e63c71SPaul Luse /* 94c5e63c71SPaul Luse * The CLI uses the SPDK app framework so is async and callback driven. A 95c5e63c71SPaul Luse * pointer to this structure is passed to SPDK calls and returned in the 96c5e63c71SPaul Luse * callbacks for easy access to all the info we may need. 97c5e63c71SPaul Luse */ 988fbbfccdSPaul Luse struct cli_context_t { 998fbbfccdSPaul Luse struct spdk_blob_store *bs; 1008fbbfccdSPaul Luse struct spdk_blob *blob; 101c5e63c71SPaul Luse struct spdk_bs_dev *bs_dev; 1028fbbfccdSPaul Luse spdk_blob_id blobid; 1038fbbfccdSPaul Luse spdk_blob_id superid; 1048fbbfccdSPaul Luse struct spdk_io_channel *channel; 1058fbbfccdSPaul Luse uint8_t *buff; 1068fbbfccdSPaul Luse uint64_t page_size; 1078fbbfccdSPaul Luse uint64_t page_count; 1088fbbfccdSPaul Luse uint64_t blob_pages; 1098fbbfccdSPaul Luse uint64_t bytes_so_far; 1108fbbfccdSPaul Luse FILE *fp; 1118fbbfccdSPaul Luse enum cli_action_type action; 1128fbbfccdSPaul Luse char key[BUFSIZE + 1]; 1138fbbfccdSPaul Luse char value[BUFSIZE + 1]; 1148fbbfccdSPaul Luse char file[BUFSIZE + 1]; 1158fbbfccdSPaul Luse uint64_t filesize; 1168fbbfccdSPaul Luse int fill_value; 117cfa4cae0SPaul Luse char bdev_name[BUFSIZE]; 1188fbbfccdSPaul Luse int rc; 1198fbbfccdSPaul Luse int num_clusters; 120c5e63c71SPaul Luse enum cli_mode_type cli_mode; 121c5e63c71SPaul Luse const char *config_file; 122c5e63c71SPaul Luse int argc; 123c5e63c71SPaul Luse char *argv[MAX_ARGS]; 124c5e63c71SPaul Luse bool app_started; 125e5044f79SPaul Luse char script_file[BUFSIZE + 1]; 1268fbbfccdSPaul Luse }; 1278fbbfccdSPaul Luse 128e5044f79SPaul Luse /* we store a bunch of stuff in a global struct for use by scripting mode */ 129e5044f79SPaul Luse #define MAX_SCRIPT_LINES 64 130e5044f79SPaul Luse #define MAX_SCRIPT_BLOBS 16 131e5044f79SPaul Luse struct cli_script_t { 132e5044f79SPaul Luse spdk_blob_id blobid[MAX_SCRIPT_BLOBS]; 133e5044f79SPaul Luse int blobid_idx; 134e5044f79SPaul Luse int max_index; 135e5044f79SPaul Luse int cmdline_idx; 136e5044f79SPaul Luse bool ignore_errors; 137e5044f79SPaul Luse char *cmdline[MAX_SCRIPT_LINES]; 138e5044f79SPaul Luse }; 139e5044f79SPaul Luse struct cli_script_t g_script; 140e5044f79SPaul Luse 1418fbbfccdSPaul Luse /* 142c5e63c71SPaul Luse * Common printing of commands for CLI and shell modes. 143c5e63c71SPaul Luse */ 144c5e63c71SPaul Luse static void 145e5044f79SPaul Luse print_cmds(void) 146c5e63c71SPaul Luse { 147c5e63c71SPaul Luse printf("\nCommands include:\n"); 148cfa4cae0SPaul Luse printf("\t-b bdev - name of the block device to use (example: Nvme0n1)\n"); 149c5e63c71SPaul Luse printf("\t-d <blobid> filename - dump contents of a blob to a file\n"); 150c5e63c71SPaul Luse printf("\t-f <blobid> value - fill a blob with a decimal value\n"); 151c5e63c71SPaul Luse printf("\t-h - this help screen\n"); 152c5e63c71SPaul Luse printf("\t-i - initialize a blobstore\n"); 153c5e63c71SPaul Luse printf("\t-l bdevs | blobs - list either available bdevs or existing blobs\n"); 154c5e63c71SPaul Luse printf("\t-m <blobid> filename - import contents of a file to a blob\n"); 155c5e63c71SPaul Luse printf("\t-n <# clusters> - create new blob\n"); 156c5e63c71SPaul Luse printf("\t-p <blobid> - set the superblob to the ID provided\n"); 157c5e63c71SPaul Luse printf("\t-r <blobid> name - remove xattr name/value pair\n"); 158c5e63c71SPaul Luse printf("\t-s <blobid> | bs - show blob info or blobstore info\n"); 159c5e63c71SPaul Luse printf("\t-x <blobid> name value - set xattr name/value pair\n"); 160c5e63c71SPaul Luse printf("\t-X - exit when in interactive shell mode\n"); 161c5e63c71SPaul Luse printf("\t-S - enter interactive shell mode\n"); 162e5044f79SPaul Luse printf("\t-T <filename> - automated script mode\n"); 163c5e63c71SPaul Luse printf("\n"); 164c5e63c71SPaul Luse } 165c5e63c71SPaul Luse 166c5e63c71SPaul Luse /* 1678fbbfccdSPaul Luse * Prints usage and relevant error message. 1688fbbfccdSPaul Luse */ 1698fbbfccdSPaul Luse static void 170e5044f79SPaul Luse usage(struct cli_context_t *cli_context, char *msg) 1718fbbfccdSPaul Luse { 1728fbbfccdSPaul Luse if (msg) { 1738fbbfccdSPaul Luse printf("%s", msg); 1748fbbfccdSPaul Luse } 175e5044f79SPaul Luse 1767fc14171SDaniel Verkamp if (!cli_context || cli_context->cli_mode == CLI_MODE_CMD) { 1778fbbfccdSPaul Luse printf("Version %s\n", SPDK_VERSION_STRING); 1788fbbfccdSPaul Luse printf("Usage: %s [-c SPDK config_file] Command\n", program_name); 1798fbbfccdSPaul Luse printf("\n%s is a command line tool for interacting with blobstore\n", 1808fbbfccdSPaul Luse program_name); 1818fbbfccdSPaul Luse printf("on the underlying device specified in the conf file passed\n"); 1828fbbfccdSPaul Luse printf("in as a command line option.\n"); 183e5044f79SPaul Luse } 1847fc14171SDaniel Verkamp if (!cli_context || cli_context->cli_mode != CLI_MODE_SCRIPT) { 185e5044f79SPaul Luse print_cmds(); 186e5044f79SPaul Luse } 1878fbbfccdSPaul Luse } 1888fbbfccdSPaul Luse 1898fbbfccdSPaul Luse /* 1908fbbfccdSPaul Luse * Free up memory that we allocated. 1918fbbfccdSPaul Luse */ 1928fbbfccdSPaul Luse static void 1938fbbfccdSPaul Luse cli_cleanup(struct cli_context_t *cli_context) 1948fbbfccdSPaul Luse { 1958fbbfccdSPaul Luse if (cli_context->buff) { 1968fbbfccdSPaul Luse spdk_dma_free(cli_context->buff); 1978fbbfccdSPaul Luse } 198e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 199e5044f79SPaul Luse int i; 200e5044f79SPaul Luse 201e5044f79SPaul Luse for (i = 0; i <= g_script.max_index; i++) { 202e5044f79SPaul Luse free(g_script.cmdline[i]); 203e5044f79SPaul Luse } 204e5044f79SPaul Luse } 2058fbbfccdSPaul Luse free(cli_context); 2068fbbfccdSPaul Luse } 2078fbbfccdSPaul Luse 2088fbbfccdSPaul Luse /* 2098fbbfccdSPaul Luse * Callback routine for the blobstore unload. 2108fbbfccdSPaul Luse */ 2118fbbfccdSPaul Luse static void 2128fbbfccdSPaul Luse unload_complete(void *cb_arg, int bserrno) 2138fbbfccdSPaul Luse { 2148fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 2158fbbfccdSPaul Luse 2168fbbfccdSPaul Luse if (bserrno) { 2178fbbfccdSPaul Luse printf("Error %d unloading the bobstore\n", bserrno); 2188fbbfccdSPaul Luse cli_context->rc = bserrno; 2198fbbfccdSPaul Luse } 2208fbbfccdSPaul Luse 221c5e63c71SPaul Luse /* 222c5e63c71SPaul Luse * Quit if we're in cmd mode or exiting shell mode, otherwise 223c5e63c71SPaul Luse * clear the action field and start the main function again. 224c5e63c71SPaul Luse */ 225c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD || 226c5e63c71SPaul Luse cli_context->action == CLI_SHELL_EXIT) { 2278fbbfccdSPaul Luse spdk_app_stop(cli_context->rc); 228c5e63c71SPaul Luse } else { 229d4e565b4SPaul Luse /* when action is CLI_NONE, we know we need to remain in the shell */ 230d4e565b4SPaul Luse cli_context->bs = NULL; 231c5e63c71SPaul Luse cli_context->action = CLI_NONE; 232c5e63c71SPaul Luse cli_start(cli_context, NULL); 233c5e63c71SPaul Luse } 2348fbbfccdSPaul Luse } 2358fbbfccdSPaul Luse 2368fbbfccdSPaul Luse /* 2378fbbfccdSPaul Luse * Unload the blobstore. 2388fbbfccdSPaul Luse */ 2398fbbfccdSPaul Luse static void 2408fbbfccdSPaul Luse unload_bs(struct cli_context_t *cli_context, char *msg, int bserrno) 2418fbbfccdSPaul Luse { 2428fbbfccdSPaul Luse if (bserrno) { 2438fbbfccdSPaul Luse printf("%s (err %d)\n", msg, bserrno); 2448fbbfccdSPaul Luse cli_context->rc = bserrno; 2458fbbfccdSPaul Luse } 246c5e63c71SPaul Luse 2478fbbfccdSPaul Luse if (cli_context->bs) { 248c5e63c71SPaul Luse if (cli_context->channel) { 249c5e63c71SPaul Luse spdk_bs_free_io_channel(cli_context->channel); 250d4e565b4SPaul Luse cli_context->channel = NULL; 251c5e63c71SPaul Luse } 2528fbbfccdSPaul Luse spdk_bs_unload(cli_context->bs, unload_complete, cli_context); 253a23fa64cSPaul Luse } else if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 2548fbbfccdSPaul Luse spdk_app_stop(bserrno); 255a23fa64cSPaul Luse 2568fbbfccdSPaul Luse } 2578fbbfccdSPaul Luse } 2588fbbfccdSPaul Luse 2598fbbfccdSPaul Luse /* 2608fbbfccdSPaul Luse * Callback for closing a blob. 2618fbbfccdSPaul Luse */ 2628fbbfccdSPaul Luse static void 2638fbbfccdSPaul Luse close_cb(void *arg1, int bserrno) 2648fbbfccdSPaul Luse { 2658fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 2668fbbfccdSPaul Luse 2678fbbfccdSPaul Luse if (bserrno) { 2688fbbfccdSPaul Luse unload_bs(cli_context, "Error in close callback", 2698fbbfccdSPaul Luse bserrno); 2708fbbfccdSPaul Luse return; 2718fbbfccdSPaul Luse } 2728fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 2738fbbfccdSPaul Luse } 2748fbbfccdSPaul Luse 2758fbbfccdSPaul Luse /* 2768fbbfccdSPaul Luse * Callback function for sync'ing metadata. 2778fbbfccdSPaul Luse */ 2788fbbfccdSPaul Luse static void 279866f15b3SPaul Luse sync_cb(void *arg1, int bserrno) 2808fbbfccdSPaul Luse { 2818fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 2828fbbfccdSPaul Luse 2838fbbfccdSPaul Luse if (bserrno) { 2848fbbfccdSPaul Luse unload_bs(cli_context, "Error in sync callback", 2858fbbfccdSPaul Luse bserrno); 2868fbbfccdSPaul Luse return; 2878fbbfccdSPaul Luse } 2888fbbfccdSPaul Luse 289e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 2908fbbfccdSPaul Luse } 2918fbbfccdSPaul Luse 2928fbbfccdSPaul Luse /* 2938fbbfccdSPaul Luse * Callback function for opening a blob after creating. 2948fbbfccdSPaul Luse */ 2958fbbfccdSPaul Luse static void 296866f15b3SPaul Luse open_now_resize_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 2978fbbfccdSPaul Luse { 2988fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 2998fbbfccdSPaul Luse int rc = 0; 3008fbbfccdSPaul Luse uint64_t total = 0; 3018fbbfccdSPaul Luse 3028fbbfccdSPaul Luse if (bserrno) { 3038fbbfccdSPaul Luse unload_bs(cli_context, "Error in open completion", 3048fbbfccdSPaul Luse bserrno); 3058fbbfccdSPaul Luse return; 3068fbbfccdSPaul Luse } 3078fbbfccdSPaul Luse cli_context->blob = blob; 3088fbbfccdSPaul Luse 3092c3591f1SJim Harris rc = spdk_blob_resize(cli_context->blob, cli_context->num_clusters); 3108fbbfccdSPaul Luse if (rc) { 3118fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob resize", 3128fbbfccdSPaul Luse bserrno); 3138fbbfccdSPaul Luse return; 3148fbbfccdSPaul Luse } 3158fbbfccdSPaul Luse 3168fbbfccdSPaul Luse total = spdk_blob_get_num_clusters(cli_context->blob); 3178fbbfccdSPaul Luse printf("blob now has USED clusters of %" PRIu64 "\n", 3188fbbfccdSPaul Luse total); 3198fbbfccdSPaul Luse 3208fbbfccdSPaul Luse /* 3218fbbfccdSPaul Luse * Always a good idea to sync after MD changes or the changes 3228fbbfccdSPaul Luse * may be lost if things aren't closed cleanly. 3238fbbfccdSPaul Luse */ 3242c3591f1SJim Harris spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context); 3258fbbfccdSPaul Luse } 3268fbbfccdSPaul Luse 3278fbbfccdSPaul Luse /* 3288fbbfccdSPaul Luse * Callback function for creating a blob. 3298fbbfccdSPaul Luse */ 3308fbbfccdSPaul Luse static void 331866f15b3SPaul Luse blob_create_cb(void *arg1, spdk_blob_id blobid, int bserrno) 3328fbbfccdSPaul Luse { 3338fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 3348fbbfccdSPaul Luse 3358fbbfccdSPaul Luse if (bserrno) { 3368fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob create callback", 3378fbbfccdSPaul Luse bserrno); 3388fbbfccdSPaul Luse return; 3398fbbfccdSPaul Luse } 3408fbbfccdSPaul Luse 3418fbbfccdSPaul Luse cli_context->blobid = blobid; 3428fbbfccdSPaul Luse printf("New blob id %" PRIu64 "\n", cli_context->blobid); 3438fbbfccdSPaul Luse 344e5044f79SPaul Luse /* if we're in script mode, we need info on all blobids for later */ 345e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 346e5044f79SPaul Luse g_script.blobid[g_script.blobid_idx++] = blobid; 347e5044f79SPaul Luse } 348e5044f79SPaul Luse 3498fbbfccdSPaul Luse /* We have to open the blob before we can do things like resize. */ 350d52dbda2SJim Harris spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 351866f15b3SPaul Luse open_now_resize_cb, cli_context); 3528fbbfccdSPaul Luse } 3538fbbfccdSPaul Luse 3548fbbfccdSPaul Luse /* 3558fbbfccdSPaul Luse * Callback for get_super where we'll continue on to show blobstore info. 3568fbbfccdSPaul Luse */ 3578fbbfccdSPaul Luse static void 358866f15b3SPaul Luse show_bs_cb(void *arg1, spdk_blob_id blobid, int bserrno) 3598fbbfccdSPaul Luse { 3608fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 361b3781b22SDaniel Verkamp struct spdk_bs_type bstype; 3628fbbfccdSPaul Luse uint64_t val; 3638fbbfccdSPaul Luse struct spdk_bdev *bdev = NULL; 3648fbbfccdSPaul Luse 3658fbbfccdSPaul Luse if (bserrno && bserrno != -ENOENT) { 3668fbbfccdSPaul Luse unload_bs(cli_context, "Error in get_super callback", 3678fbbfccdSPaul Luse bserrno); 3688fbbfccdSPaul Luse return; 3698fbbfccdSPaul Luse } 3708fbbfccdSPaul Luse cli_context->superid = blobid; 3718fbbfccdSPaul Luse 3728fbbfccdSPaul Luse bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 3738fbbfccdSPaul Luse if (bdev == NULL) { 3748fbbfccdSPaul Luse unload_bs(cli_context, "Error w/bdev in get_super callback", 3758fbbfccdSPaul Luse bserrno); 3768fbbfccdSPaul Luse return; 3778fbbfccdSPaul Luse } 3788fbbfccdSPaul Luse 3798fbbfccdSPaul Luse printf("Blobstore Public Info:\n"); 3808fbbfccdSPaul Luse printf("\tUsing bdev Product Name: %s\n", 3818fbbfccdSPaul Luse spdk_bdev_get_product_name(bdev)); 3828fbbfccdSPaul Luse printf("\tAPI Version: %d\n", SPDK_BS_VERSION); 3838fbbfccdSPaul Luse 3848fbbfccdSPaul Luse if (bserrno != -ENOENT) { 3858fbbfccdSPaul Luse printf("\tsuper blob ID: %" PRIu64 "\n", cli_context->superid); 3868fbbfccdSPaul Luse } else { 3878fbbfccdSPaul Luse printf("\tsuper blob ID: none assigned\n"); 3888fbbfccdSPaul Luse } 3898fbbfccdSPaul Luse 390866f15b3SPaul Luse printf("\tpage size: %" PRIu64 "\n", cli_context->page_size); 3918fbbfccdSPaul Luse 3928fbbfccdSPaul Luse val = spdk_bs_get_cluster_size(cli_context->bs); 3938fbbfccdSPaul Luse printf("\tcluster size: %" PRIu64 "\n", val); 3948fbbfccdSPaul Luse 3958fbbfccdSPaul Luse val = spdk_bs_free_cluster_count(cli_context->bs); 3968fbbfccdSPaul Luse printf("\t# free clusters: %" PRIu64 "\n", val); 3978fbbfccdSPaul Luse 398b3781b22SDaniel Verkamp bstype = spdk_bs_get_bstype(cli_context->bs); 399b3781b22SDaniel Verkamp spdk_trace_dump(stdout, "\tblobstore type:", &bstype, sizeof(bstype)); 400b3781b22SDaniel Verkamp 4018fbbfccdSPaul Luse /* 4028fbbfccdSPaul Luse * Private info isn't accessible via the public API but 4038fbbfccdSPaul Luse * may be useful for debug of blobstore based applications. 4048fbbfccdSPaul Luse */ 4058fbbfccdSPaul Luse printf("\nBlobstore Private Info:\n"); 4068fbbfccdSPaul Luse printf("\tMetadata start (pages): %" PRIu64 "\n", 4078fbbfccdSPaul Luse cli_context->bs->md_start); 4088fbbfccdSPaul Luse printf("\tMetadata length (pages): %d\n", 4098fbbfccdSPaul Luse cli_context->bs->md_len); 4108fbbfccdSPaul Luse 4118fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 4128fbbfccdSPaul Luse } 4138fbbfccdSPaul Luse 4148fbbfccdSPaul Luse /* 4158fbbfccdSPaul Luse * Show detailed info about a particular blob. 4168fbbfccdSPaul Luse */ 4178fbbfccdSPaul Luse static void 4188fbbfccdSPaul Luse show_blob(struct cli_context_t *cli_context) 4198fbbfccdSPaul Luse { 4208fbbfccdSPaul Luse uint64_t val; 4218fbbfccdSPaul Luse struct spdk_xattr_names *names; 4228fbbfccdSPaul Luse const void *value; 4238fbbfccdSPaul Luse size_t value_len; 424866f15b3SPaul Luse char data[BUFSIZE]; 4258fbbfccdSPaul Luse unsigned int i; 4268fbbfccdSPaul Luse 4278fbbfccdSPaul Luse printf("Blob Public Info:\n"); 4288fbbfccdSPaul Luse 4298fbbfccdSPaul Luse printf("blob ID: %" PRIu64 "\n", cli_context->blobid); 4308fbbfccdSPaul Luse 4318fbbfccdSPaul Luse val = spdk_blob_get_num_clusters(cli_context->blob); 4328fbbfccdSPaul Luse printf("# of clusters: %" PRIu64 "\n", val); 4338fbbfccdSPaul Luse 4348fbbfccdSPaul Luse printf("# of bytes: %" PRIu64 "\n", 4358fbbfccdSPaul Luse val * spdk_bs_get_cluster_size(cli_context->bs)); 4368fbbfccdSPaul Luse 4378fbbfccdSPaul Luse val = spdk_blob_get_num_pages(cli_context->blob); 4388fbbfccdSPaul Luse printf("# of pages: %" PRIu64 "\n", val); 4398fbbfccdSPaul Luse 4402c3591f1SJim Harris spdk_blob_get_xattr_names(cli_context->blob, &names); 4418fbbfccdSPaul Luse 4428fbbfccdSPaul Luse printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names)); 4438fbbfccdSPaul Luse printf("xattrs:\n"); 4448fbbfccdSPaul Luse for (i = 0; i < spdk_xattr_names_get_count(names); i++) { 4452c3591f1SJim Harris spdk_blob_get_xattr_value(cli_context->blob, 4468fbbfccdSPaul Luse spdk_xattr_names_get_name(names, i), 4478fbbfccdSPaul Luse &value, &value_len); 4488fbbfccdSPaul Luse if ((value_len + 1) > sizeof(data)) { 4498fbbfccdSPaul Luse printf("FYI: adjusting size of xattr due to CLI limits.\n"); 4508fbbfccdSPaul Luse value_len = sizeof(data) - 1; 4518fbbfccdSPaul Luse } 4528fbbfccdSPaul Luse memcpy(&data, value, value_len); 4538fbbfccdSPaul Luse data[value_len] = '\0'; 4548fbbfccdSPaul Luse printf("\n(%d) Name:%s\n", i, 4558fbbfccdSPaul Luse spdk_xattr_names_get_name(names, i)); 4568fbbfccdSPaul Luse printf("(%d) Value:\n", i); 4578fbbfccdSPaul Luse spdk_trace_dump(stdout, "", value, value_len); 4588fbbfccdSPaul Luse } 4598fbbfccdSPaul Luse 4608fbbfccdSPaul Luse /* 4618fbbfccdSPaul Luse * Private info isn't accessible via the public API but 4628fbbfccdSPaul Luse * may be useful for debug of blobstore based applications. 4638fbbfccdSPaul Luse */ 4648fbbfccdSPaul Luse printf("\nBlob Private Info:\n"); 465*c8efd8a8SJim Harris switch (cli_context->blob->state) { 4668fbbfccdSPaul Luse case SPDK_BLOB_STATE_DIRTY: 4678fbbfccdSPaul Luse printf("state: DIRTY\n"); 4688fbbfccdSPaul Luse break; 4698fbbfccdSPaul Luse case SPDK_BLOB_STATE_CLEAN: 4708fbbfccdSPaul Luse printf("state: CLEAN\n"); 4718fbbfccdSPaul Luse break; 4728fbbfccdSPaul Luse case SPDK_BLOB_STATE_LOADING: 4738fbbfccdSPaul Luse printf("state: LOADING\n"); 4748fbbfccdSPaul Luse break; 4758fbbfccdSPaul Luse case SPDK_BLOB_STATE_SYNCING: 4768fbbfccdSPaul Luse printf("state: SYNCING\n"); 4778fbbfccdSPaul Luse break; 4788fbbfccdSPaul Luse default: 4798fbbfccdSPaul Luse printf("state: UNKNOWN\n"); 4808fbbfccdSPaul Luse break; 4818fbbfccdSPaul Luse } 4828fbbfccdSPaul Luse printf("open ref count: %d\n", 483*c8efd8a8SJim Harris cli_context->blob->open_ref); 484c5e63c71SPaul Luse 485c5e63c71SPaul Luse spdk_xattr_names_free(names); 4868fbbfccdSPaul Luse } 4878fbbfccdSPaul Luse 4888fbbfccdSPaul Luse /* 489866f15b3SPaul Luse * Callback for getting the first blob, shared with simple blob listing as well. 4908fbbfccdSPaul Luse */ 4918fbbfccdSPaul Luse static void 4928fbbfccdSPaul Luse blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno) 4938fbbfccdSPaul Luse { 4948fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 4958fbbfccdSPaul Luse 4968fbbfccdSPaul Luse if (bserrno) { 4978fbbfccdSPaul Luse if (bserrno == -ENOENT) { 4988fbbfccdSPaul Luse /* this simply means there are no more blobs */ 4998fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 5008fbbfccdSPaul Luse } else { 5018fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob iter callback", 5028fbbfccdSPaul Luse bserrno); 5038fbbfccdSPaul Luse } 5048fbbfccdSPaul Luse return; 5058fbbfccdSPaul Luse } 5068fbbfccdSPaul Luse 5078fbbfccdSPaul Luse if (cli_context->action == CLI_LIST_BLOBS) { 508866f15b3SPaul Luse printf("\nList BLOBS:\n"); 5098fbbfccdSPaul Luse printf("Found blob with ID# %" PRIu64 "\n", 5108fbbfccdSPaul Luse spdk_blob_get_id(blob)); 5118fbbfccdSPaul Luse } else if (spdk_blob_get_id(blob) == cli_context->blobid) { 5128fbbfccdSPaul Luse /* 5138fbbfccdSPaul Luse * Found the blob we're looking for, but we need to finish 5148fbbfccdSPaul Luse * iterating even after showing the info so that internally 5158fbbfccdSPaul Luse * the blobstore logic will close the blob. Or we could 5168fbbfccdSPaul Luse * chose to close it now, either way. 5178fbbfccdSPaul Luse */ 5188fbbfccdSPaul Luse cli_context->blob = blob; 5198fbbfccdSPaul Luse show_blob(cli_context); 5208fbbfccdSPaul Luse } 5218fbbfccdSPaul Luse 522ae5a01ddSJim Harris spdk_bs_iter_next(cli_context->bs, blob, blob_iter_cb, cli_context); 5238fbbfccdSPaul Luse } 5248fbbfccdSPaul Luse 5258fbbfccdSPaul Luse /* 5268fbbfccdSPaul Luse * Callback for setting the super blob ID. 5278fbbfccdSPaul Luse */ 5288fbbfccdSPaul Luse static void 5298fbbfccdSPaul Luse set_super_cb(void *arg1, int bserrno) 5308fbbfccdSPaul Luse { 5318fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 5328fbbfccdSPaul Luse 5338fbbfccdSPaul Luse if (bserrno) { 5348fbbfccdSPaul Luse unload_bs(cli_context, "Error in set_super callback", 5358fbbfccdSPaul Luse bserrno); 5368fbbfccdSPaul Luse return; 5378fbbfccdSPaul Luse } 5388fbbfccdSPaul Luse 5398fbbfccdSPaul Luse printf("Super Blob ID has been set.\n"); 5408fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 5418fbbfccdSPaul Luse } 5428fbbfccdSPaul Luse 5438fbbfccdSPaul Luse /* 5448fbbfccdSPaul Luse * Callback for set_xattr_open where we set or delete xattrs. 5458fbbfccdSPaul Luse */ 5468fbbfccdSPaul Luse static void 547866f15b3SPaul Luse set_xattr_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 5488fbbfccdSPaul Luse { 5498fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 5508fbbfccdSPaul Luse 5518fbbfccdSPaul Luse if (bserrno) { 5528fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob open callback", 5538fbbfccdSPaul Luse bserrno); 5548fbbfccdSPaul Luse return; 5558fbbfccdSPaul Luse } 5568fbbfccdSPaul Luse cli_context->blob = blob; 5578fbbfccdSPaul Luse 5588fbbfccdSPaul Luse if (cli_context->action == CLI_SET_XATTR) { 5592c3591f1SJim Harris spdk_blob_set_xattr(cli_context->blob, cli_context->key, 5602c3591f1SJim Harris cli_context->value, strlen(cli_context->value) + 1); 5618fbbfccdSPaul Luse printf("Xattr has been set.\n"); 5628fbbfccdSPaul Luse } else { 5632c3591f1SJim Harris spdk_blob_remove_xattr(cli_context->blob, cli_context->key); 5648fbbfccdSPaul Luse printf("Xattr has been removed.\n"); 5658fbbfccdSPaul Luse } 5668fbbfccdSPaul Luse 5672c3591f1SJim Harris spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context); 5688fbbfccdSPaul Luse } 5698fbbfccdSPaul Luse 5708fbbfccdSPaul Luse /* 5718fbbfccdSPaul Luse * Callback function for reading a blob for dumping to a file. 5728fbbfccdSPaul Luse */ 5738fbbfccdSPaul Luse static void 574866f15b3SPaul Luse read_dump_cb(void *arg1, int bserrno) 5758fbbfccdSPaul Luse { 5768fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 5778fbbfccdSPaul Luse uint64_t bytes_written; 5788fbbfccdSPaul Luse 5798fbbfccdSPaul Luse if (bserrno) { 5808fbbfccdSPaul Luse fclose(cli_context->fp); 5818fbbfccdSPaul Luse unload_bs(cli_context, "Error in read completion", 5828fbbfccdSPaul Luse bserrno); 5838fbbfccdSPaul Luse return; 5848fbbfccdSPaul Luse } 5858fbbfccdSPaul Luse 586866f15b3SPaul Luse bytes_written = fwrite(cli_context->buff, NUM_PAGES, cli_context->page_size, 5878fbbfccdSPaul Luse cli_context->fp); 5888fbbfccdSPaul Luse if (bytes_written != cli_context->page_size) { 5898fbbfccdSPaul Luse fclose(cli_context->fp); 5908fbbfccdSPaul Luse unload_bs(cli_context, "Error with fwrite", 5918fbbfccdSPaul Luse bserrno); 5928fbbfccdSPaul Luse return; 5938fbbfccdSPaul Luse } 5948fbbfccdSPaul Luse 5958fbbfccdSPaul Luse printf("."); 5968fbbfccdSPaul Luse if (++cli_context->page_count < cli_context->blob_pages) { 5978fbbfccdSPaul Luse /* perform another read */ 5988fbbfccdSPaul Luse spdk_bs_io_read_blob(cli_context->blob, cli_context->channel, 5998fbbfccdSPaul Luse cli_context->buff, cli_context->page_count, 600866f15b3SPaul Luse NUM_PAGES, read_dump_cb, cli_context); 6018fbbfccdSPaul Luse } else { 6028fbbfccdSPaul Luse /* done reading */ 603e9a50eb8SPaul Luse printf("\nFile write complete (to %s).\n", cli_context->file); 6048fbbfccdSPaul Luse fclose(cli_context->fp); 605e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 6068fbbfccdSPaul Luse } 6078fbbfccdSPaul Luse } 6088fbbfccdSPaul Luse 6098fbbfccdSPaul Luse /* 6108fbbfccdSPaul Luse * Callback for write completion on the import of a file to a blob. 6118fbbfccdSPaul Luse */ 6128fbbfccdSPaul Luse static void 613866f15b3SPaul Luse write_imp_cb(void *arg1, int bserrno) 6148fbbfccdSPaul Luse { 6158fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 6168fbbfccdSPaul Luse uint64_t bytes_read; 6178fbbfccdSPaul Luse 6188fbbfccdSPaul Luse if (bserrno) { 6198fbbfccdSPaul Luse fclose(cli_context->fp); 6208fbbfccdSPaul Luse unload_bs(cli_context, "Error in write completion", 6218fbbfccdSPaul Luse bserrno); 6228fbbfccdSPaul Luse return; 6238fbbfccdSPaul Luse } 6248fbbfccdSPaul Luse 6258fbbfccdSPaul Luse if (cli_context->bytes_so_far < cli_context->filesize) { 6268fbbfccdSPaul Luse /* perform another file read */ 6278fbbfccdSPaul Luse bytes_read = fread(cli_context->buff, 1, 6288fbbfccdSPaul Luse cli_context->page_size, 6298fbbfccdSPaul Luse cli_context->fp); 6308fbbfccdSPaul Luse cli_context->bytes_so_far += bytes_read; 6318fbbfccdSPaul Luse 6328fbbfccdSPaul Luse /* if this read is < 1 page, fill with 0s */ 6338fbbfccdSPaul Luse if (bytes_read < cli_context->page_size) { 6348fbbfccdSPaul Luse uint8_t *offset = cli_context->buff + bytes_read; 635866f15b3SPaul Luse memset(offset, 0, cli_context->page_size - bytes_read); 6368fbbfccdSPaul Luse } 6378fbbfccdSPaul Luse } else { 6388fbbfccdSPaul Luse /* 6398fbbfccdSPaul Luse * Done reading the file, fill the rest of the blob with 0s, 6408fbbfccdSPaul Luse * yeah we're memsetting the same page over and over here 6418fbbfccdSPaul Luse */ 6428fbbfccdSPaul Luse memset(cli_context->buff, 0, cli_context->page_size); 6438fbbfccdSPaul Luse } 6448fbbfccdSPaul Luse if (++cli_context->page_count < cli_context->blob_pages) { 6458fbbfccdSPaul Luse printf("."); 6468fbbfccdSPaul Luse spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 6478fbbfccdSPaul Luse cli_context->buff, cli_context->page_count, 648866f15b3SPaul Luse NUM_PAGES, write_imp_cb, cli_context); 6498fbbfccdSPaul Luse } else { 6508fbbfccdSPaul Luse /* done writing */ 651e9a50eb8SPaul Luse printf("\nBlob import complete (from %s).\n", cli_context->file); 6528fbbfccdSPaul Luse fclose(cli_context->fp); 653e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 6548fbbfccdSPaul Luse } 6558fbbfccdSPaul Luse } 6568fbbfccdSPaul Luse 6578fbbfccdSPaul Luse /* 6588fbbfccdSPaul Luse * Callback for open blobs where we'll continue on dump a blob to a file or 6598fbbfccdSPaul Luse * import a file to a blob. For dump, the resulting file will always be the 6608fbbfccdSPaul Luse * full size of the blob. For import, the blob will fill with the file 6618fbbfccdSPaul Luse * contents first and then 0 out the rest of the blob. 6628fbbfccdSPaul Luse */ 6638fbbfccdSPaul Luse static void 664866f15b3SPaul Luse dump_imp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 6658fbbfccdSPaul Luse { 6668fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 6678fbbfccdSPaul Luse 6688fbbfccdSPaul Luse if (bserrno) { 6698fbbfccdSPaul Luse unload_bs(cli_context, "Error in blob open callback", 6708fbbfccdSPaul Luse bserrno); 6718fbbfccdSPaul Luse return; 6728fbbfccdSPaul Luse } 6738fbbfccdSPaul Luse cli_context->blob = blob; 6748fbbfccdSPaul Luse 6758fbbfccdSPaul Luse /* 6768fbbfccdSPaul Luse * We'll transfer just one page at a time to keep the buffer 6778fbbfccdSPaul Luse * small. This could be bigger of course. 6788fbbfccdSPaul Luse */ 6798fbbfccdSPaul Luse cli_context->buff = spdk_dma_malloc(cli_context->page_size, 680866f15b3SPaul Luse ALIGN_4K, NULL); 6818fbbfccdSPaul Luse if (cli_context->buff == NULL) { 682809dc418SPaul Luse printf("Error in allocating memory\n"); 683e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 6848fbbfccdSPaul Luse return; 6858fbbfccdSPaul Luse } 6868fbbfccdSPaul Luse printf("Working"); 6878fbbfccdSPaul Luse cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob); 6888fbbfccdSPaul Luse cli_context->page_count = 0; 6894952f254SDaniel Verkamp if (cli_context->action == CLI_DUMP_BLOB) { 6908fbbfccdSPaul Luse cli_context->fp = fopen(cli_context->file, "w"); 691809dc418SPaul Luse if (cli_context->fp == NULL) { 692809dc418SPaul Luse printf("Error in opening file\n"); 693e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 694809dc418SPaul Luse return; 695809dc418SPaul Luse } 6968fbbfccdSPaul Luse 6978fbbfccdSPaul Luse /* read a page of data from the blob */ 6988fbbfccdSPaul Luse spdk_bs_io_read_blob(cli_context->blob, cli_context->channel, 6998fbbfccdSPaul Luse cli_context->buff, cli_context->page_count, 700866f15b3SPaul Luse NUM_PAGES, read_dump_cb, cli_context); 7018fbbfccdSPaul Luse } else { 7028fbbfccdSPaul Luse cli_context->fp = fopen(cli_context->file, "r"); 703809dc418SPaul Luse if (cli_context->fp == NULL) { 704a0e22e13Spaul luse printf("Error in opening file: errno %d\n", errno); 705e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 706809dc418SPaul Luse return; 707809dc418SPaul Luse } 7088fbbfccdSPaul Luse 7098fbbfccdSPaul Luse /* get the filesize then rewind read a page of data from file */ 7108fbbfccdSPaul Luse fseek(cli_context->fp, 0L, SEEK_END); 7118fbbfccdSPaul Luse cli_context->filesize = ftell(cli_context->fp); 7128fbbfccdSPaul Luse rewind(cli_context->fp); 713866f15b3SPaul Luse cli_context->bytes_so_far = fread(cli_context->buff, NUM_PAGES, 7148fbbfccdSPaul Luse cli_context->page_size, 7158fbbfccdSPaul Luse cli_context->fp); 7168fbbfccdSPaul Luse 7178fbbfccdSPaul Luse /* if the file is < a page, fill the rest with 0s */ 7188fbbfccdSPaul Luse if (cli_context->filesize < cli_context->page_size) { 7198fbbfccdSPaul Luse uint8_t *offset = 7208fbbfccdSPaul Luse cli_context->buff + cli_context->filesize; 7218fbbfccdSPaul Luse 7228fbbfccdSPaul Luse memset(offset, 0, 7238fbbfccdSPaul Luse cli_context->page_size - cli_context->filesize); 7248fbbfccdSPaul Luse } 7258fbbfccdSPaul Luse 7268fbbfccdSPaul Luse spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 7278fbbfccdSPaul Luse cli_context->buff, cli_context->page_count, 728866f15b3SPaul Luse NUM_PAGES, write_imp_cb, cli_context); 7298fbbfccdSPaul Luse } 7308fbbfccdSPaul Luse } 7318fbbfccdSPaul Luse 7328fbbfccdSPaul Luse /* 7338fbbfccdSPaul Luse * Callback function for writing a specific pattern to page 0. 7348fbbfccdSPaul Luse */ 7358fbbfccdSPaul Luse static void 736866f15b3SPaul Luse write_cb(void *arg1, int bserrno) 7378fbbfccdSPaul Luse { 7388fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 7398fbbfccdSPaul Luse 7408fbbfccdSPaul Luse if (bserrno) { 7418fbbfccdSPaul Luse unload_bs(cli_context, "Error in write completion", 7428fbbfccdSPaul Luse bserrno); 7438fbbfccdSPaul Luse return; 7448fbbfccdSPaul Luse } 7458fbbfccdSPaul Luse printf("."); 7468fbbfccdSPaul Luse if (++cli_context->page_count < cli_context->blob_pages) { 7478fbbfccdSPaul Luse spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 7488fbbfccdSPaul Luse cli_context->buff, cli_context->page_count, 749866f15b3SPaul Luse NUM_PAGES, write_cb, cli_context); 7508fbbfccdSPaul Luse } else { 7518fbbfccdSPaul Luse /* done writing */ 752e9a50eb8SPaul Luse printf("\nBlob fill complete (with 0x%x).\n", cli_context->fill_value); 753e734bb9fSJim Harris spdk_blob_close(cli_context->blob, close_cb, cli_context); 7548fbbfccdSPaul Luse } 7558fbbfccdSPaul Luse } 7568fbbfccdSPaul Luse 7578fbbfccdSPaul Luse /* 758866f15b3SPaul Luse * Callback function to fill a blob with a value, callback from open. 7598fbbfccdSPaul Luse */ 7608fbbfccdSPaul Luse static void 761866f15b3SPaul Luse fill_blob_cb(void *arg1, struct spdk_blob *blob, int bserrno) 7628fbbfccdSPaul Luse { 763866f15b3SPaul Luse struct cli_context_t *cli_context = arg1; 7648fbbfccdSPaul Luse 7658fbbfccdSPaul Luse if (bserrno) { 766866f15b3SPaul Luse unload_bs(cli_context, "Error in open callback", 7678fbbfccdSPaul Luse bserrno); 7688fbbfccdSPaul Luse return; 7698fbbfccdSPaul Luse } 770866f15b3SPaul Luse 7718fbbfccdSPaul Luse cli_context->blob = blob; 7728fbbfccdSPaul Luse cli_context->page_count = 0; 7738fbbfccdSPaul Luse cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob); 7748fbbfccdSPaul Luse cli_context->buff = spdk_dma_malloc(cli_context->page_size, 775866f15b3SPaul Luse ALIGN_4K, NULL); 7768fbbfccdSPaul Luse if (cli_context->buff == NULL) { 7778fbbfccdSPaul Luse unload_bs(cli_context, "Error in allocating memory", 7788fbbfccdSPaul Luse -ENOMEM); 7798fbbfccdSPaul Luse return; 7808fbbfccdSPaul Luse } 7818fbbfccdSPaul Luse 7828fbbfccdSPaul Luse memset(cli_context->buff, cli_context->fill_value, 7838fbbfccdSPaul Luse cli_context->page_size); 784e5044f79SPaul Luse printf("Working"); 7858fbbfccdSPaul Luse spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 7868fbbfccdSPaul Luse cli_context->buff, 787866f15b3SPaul Luse STARTING_PAGE, NUM_PAGES, write_cb, cli_context); 7888fbbfccdSPaul Luse } 7898fbbfccdSPaul Luse 7908fbbfccdSPaul Luse /* 791866f15b3SPaul Luse * Multiple actions require us to open the bs first so here we use 792866f15b3SPaul Luse * a common callback to set a bunch of values and then move on to 793866f15b3SPaul Luse * the next step saved off via function pointer. 7948fbbfccdSPaul Luse */ 7958fbbfccdSPaul Luse static void 796866f15b3SPaul Luse load_bs_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 7978fbbfccdSPaul Luse { 7988fbbfccdSPaul Luse struct cli_context_t *cli_context = arg1; 7998fbbfccdSPaul Luse 8008fbbfccdSPaul Luse if (bserrno) { 8018fbbfccdSPaul Luse unload_bs(cli_context, "Error in load callback", 8028fbbfccdSPaul Luse bserrno); 8038fbbfccdSPaul Luse return; 8048fbbfccdSPaul Luse } 805866f15b3SPaul Luse 8068fbbfccdSPaul Luse cli_context->bs = bs; 8078fbbfccdSPaul Luse cli_context->page_size = spdk_bs_get_page_size(cli_context->bs); 8088fbbfccdSPaul Luse cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs); 8098fbbfccdSPaul Luse if (cli_context->channel == NULL) { 8108fbbfccdSPaul Luse unload_bs(cli_context, "Error in allocating channel", 8118fbbfccdSPaul Luse -ENOMEM); 8128fbbfccdSPaul Luse return; 8138fbbfccdSPaul Luse } 8148fbbfccdSPaul Luse 815866f15b3SPaul Luse switch (cli_context->action) { 816866f15b3SPaul Luse case CLI_SET_SUPER: 817866f15b3SPaul Luse spdk_bs_set_super(cli_context->bs, cli_context->superid, 818866f15b3SPaul Luse set_super_cb, cli_context); 819866f15b3SPaul Luse break; 820866f15b3SPaul Luse case CLI_SHOW_BS: 821866f15b3SPaul Luse spdk_bs_get_super(cli_context->bs, show_bs_cb, cli_context); 822866f15b3SPaul Luse break; 823866f15b3SPaul Luse case CLI_CREATE_BLOB: 824d52dbda2SJim Harris spdk_bs_create_blob(cli_context->bs, blob_create_cb, cli_context); 825866f15b3SPaul Luse break; 826866f15b3SPaul Luse case CLI_SET_XATTR: 827866f15b3SPaul Luse case CLI_REM_XATTR: 828d52dbda2SJim Harris spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 829866f15b3SPaul Luse set_xattr_cb, cli_context); 830866f15b3SPaul Luse break; 831866f15b3SPaul Luse case CLI_SHOW_BLOB: 832866f15b3SPaul Luse case CLI_LIST_BLOBS: 833d52dbda2SJim Harris spdk_bs_iter_first(cli_context->bs, blob_iter_cb, cli_context); 834866f15b3SPaul Luse 835866f15b3SPaul Luse break; 8364952f254SDaniel Verkamp case CLI_DUMP_BLOB: 8374952f254SDaniel Verkamp case CLI_IMPORT_BLOB: 838d52dbda2SJim Harris spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 839866f15b3SPaul Luse dump_imp_open_cb, cli_context); 840866f15b3SPaul Luse break; 841866f15b3SPaul Luse case CLI_FILL: 842d52dbda2SJim Harris spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 843866f15b3SPaul Luse fill_blob_cb, cli_context); 844866f15b3SPaul Luse break; 845866f15b3SPaul Luse 846866f15b3SPaul Luse default: 847866f15b3SPaul Luse /* should never get here */ 84898a2ec3eSPaul Luse exit(-1); 849866f15b3SPaul Luse break; 850866f15b3SPaul Luse } 8518fbbfccdSPaul Luse } 8528fbbfccdSPaul Luse 8538fbbfccdSPaul Luse /* 854866f15b3SPaul Luse * Load the blobstore. 8558fbbfccdSPaul Luse */ 8568fbbfccdSPaul Luse static void 8578fbbfccdSPaul Luse load_bs(struct cli_context_t *cli_context) 8588fbbfccdSPaul Luse { 8598fbbfccdSPaul Luse struct spdk_bdev *bdev = NULL; 8608fbbfccdSPaul Luse struct spdk_bs_dev *bs_dev = NULL; 8618fbbfccdSPaul Luse 8628fbbfccdSPaul Luse bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 8638fbbfccdSPaul Luse if (bdev == NULL) { 8648fbbfccdSPaul Luse printf("Could not find a bdev\n"); 8658fbbfccdSPaul Luse spdk_app_stop(-1); 8668fbbfccdSPaul Luse return; 8678fbbfccdSPaul Luse } 8688fbbfccdSPaul Luse 869e9cf3ecfSDaniel Verkamp bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 8708fbbfccdSPaul Luse if (bs_dev == NULL) { 8718fbbfccdSPaul Luse printf("Could not create blob bdev!!\n"); 8728fbbfccdSPaul Luse spdk_app_stop(-1); 8738fbbfccdSPaul Luse return; 8748fbbfccdSPaul Luse } 8758fbbfccdSPaul Luse 876866f15b3SPaul Luse spdk_bs_load(bs_dev, NULL, load_bs_cb, cli_context); 8778fbbfccdSPaul Luse } 8788fbbfccdSPaul Luse 8798fbbfccdSPaul Luse /* 8808fbbfccdSPaul Luse * Lists all the blobs on this blobstore. 8818fbbfccdSPaul Luse */ 8828fbbfccdSPaul Luse static void 883c5e63c71SPaul Luse list_bdevs(struct cli_context_t *cli_context) 8848fbbfccdSPaul Luse { 8858fbbfccdSPaul Luse struct spdk_bdev *bdev = NULL; 8868fbbfccdSPaul Luse 8878fbbfccdSPaul Luse printf("\nList bdevs:\n"); 8888fbbfccdSPaul Luse 8898fbbfccdSPaul Luse bdev = spdk_bdev_first(); 8908fbbfccdSPaul Luse if (bdev == NULL) { 8918fbbfccdSPaul Luse printf("Could not find a bdev\n"); 8928fbbfccdSPaul Luse } 8938fbbfccdSPaul Luse while (bdev) { 8948fbbfccdSPaul Luse printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev)); 8958fbbfccdSPaul Luse printf("\tbdev Product Name: %s\n", 8968fbbfccdSPaul Luse spdk_bdev_get_product_name(bdev)); 8978fbbfccdSPaul Luse bdev = spdk_bdev_next(bdev); 8988fbbfccdSPaul Luse } 8998fbbfccdSPaul Luse 9008fbbfccdSPaul Luse printf("\n"); 901c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD) { 9028fbbfccdSPaul Luse spdk_app_stop(0); 903c5e63c71SPaul Luse } else { 904c5e63c71SPaul Luse cli_context->action = CLI_NONE; 905c5e63c71SPaul Luse cli_start(cli_context, NULL); 906c5e63c71SPaul Luse } 9078fbbfccdSPaul Luse } 9088fbbfccdSPaul Luse 9098fbbfccdSPaul Luse /* 9108fbbfccdSPaul Luse * Callback function for initializing a blob. 9118fbbfccdSPaul Luse */ 9128fbbfccdSPaul Luse static void 913866f15b3SPaul Luse bs_init_cb(void *cb_arg, struct spdk_blob_store *bs, 9148fbbfccdSPaul Luse int bserrno) 9158fbbfccdSPaul Luse { 9168fbbfccdSPaul Luse struct cli_context_t *cli_context = cb_arg; 9178fbbfccdSPaul Luse 9188fbbfccdSPaul Luse if (bserrno) { 9198fbbfccdSPaul Luse unload_bs(cli_context, "Error in bs init callback", 9208fbbfccdSPaul Luse bserrno); 9218fbbfccdSPaul Luse return; 9228fbbfccdSPaul Luse } 9238fbbfccdSPaul Luse cli_context->bs = bs; 9248fbbfccdSPaul Luse printf("blobstore init'd: (%p)\n", cli_context->bs); 9258fbbfccdSPaul Luse 9268fbbfccdSPaul Luse unload_bs(cli_context, "", 0); 9278fbbfccdSPaul Luse } 9288fbbfccdSPaul Luse 9298fbbfccdSPaul Luse /* 9308fbbfccdSPaul Luse * Initialize a new blobstore. 9318fbbfccdSPaul Luse */ 9328fbbfccdSPaul Luse static void 9338fbbfccdSPaul Luse init_bs(struct cli_context_t *cli_context) 9348fbbfccdSPaul Luse { 9358fbbfccdSPaul Luse struct spdk_bdev *bdev = NULL; 9368fbbfccdSPaul Luse 9378fbbfccdSPaul Luse bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 9388fbbfccdSPaul Luse if (bdev == NULL) { 9398fbbfccdSPaul Luse printf("Could not find a bdev\n"); 9408fbbfccdSPaul Luse spdk_app_stop(-1); 9418fbbfccdSPaul Luse return; 9428fbbfccdSPaul Luse } 943866f15b3SPaul Luse printf("Init blobstore using bdev Product Name: %s\n", 9448fbbfccdSPaul Luse spdk_bdev_get_product_name(bdev)); 9458fbbfccdSPaul Luse 946c5e63c71SPaul Luse cli_context->bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 947c5e63c71SPaul Luse if (cli_context->bs_dev == NULL) { 9488fbbfccdSPaul Luse printf("Could not create blob bdev!!\n"); 9498fbbfccdSPaul Luse spdk_app_stop(-1); 9508fbbfccdSPaul Luse return; 9518fbbfccdSPaul Luse } 9528fbbfccdSPaul Luse 953866f15b3SPaul Luse spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb, 9548fbbfccdSPaul Luse cli_context); 9558fbbfccdSPaul Luse } 9568fbbfccdSPaul Luse 9578fbbfccdSPaul Luse /* 958c5e63c71SPaul Luse * Common cmd/option parser for command and shell modes. 959c5e63c71SPaul Luse */ 960c5e63c71SPaul Luse static bool 961c5e63c71SPaul Luse cmd_parser(int argc, char **argv, struct cli_context_t *cli_context) 9628fbbfccdSPaul Luse { 9638fbbfccdSPaul Luse int op; 964c5e63c71SPaul Luse int cmd_chosen = 0; 965c5e63c71SPaul Luse char resp; 9668fbbfccdSPaul Luse 967cfa4cae0SPaul Luse while ((op = getopt(argc, argv, "b:c:d:f:hil:m:n:p:r:s:ST:Xx:")) != -1) { 9688fbbfccdSPaul Luse switch (op) { 969cfa4cae0SPaul Luse case 'b': 970cfa4cae0SPaul Luse if (strcmp(cli_context->bdev_name, "") == 0) { 971cfa4cae0SPaul Luse snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg); 972cfa4cae0SPaul Luse } else { 973cfa4cae0SPaul Luse printf("Current setting for -b is: %s\n", cli_context->bdev_name); 974cfa4cae0SPaul Luse usage(cli_context, "ERROR: -b option can only be set once.\n"); 975cfa4cae0SPaul Luse } 976cfa4cae0SPaul Luse break; 9778fbbfccdSPaul Luse case 'c': 978c5e63c71SPaul Luse if (cli_context->app_started == false) { 979c5e63c71SPaul Luse cli_context->config_file = optarg; 980c5e63c71SPaul Luse } else { 981e5044f79SPaul Luse usage(cli_context, "ERROR: -c option not valid during shell mode.\n"); 982c5e63c71SPaul Luse } 9838fbbfccdSPaul Luse break; 9848fbbfccdSPaul Luse case 'd': 985cf0d7736SPaul Luse if (argv[optind] != NULL) { 986c5e63c71SPaul Luse cmd_chosen++; 9874952f254SDaniel Verkamp cli_context->action = CLI_DUMP_BLOB; 9888fbbfccdSPaul Luse cli_context->blobid = atoll(optarg); 989e5044f79SPaul Luse snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 990cf0d7736SPaul Luse } else { 991cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 992cf0d7736SPaul Luse } 9938fbbfccdSPaul Luse break; 9948fbbfccdSPaul Luse case 'f': 995cf0d7736SPaul Luse if (argv[optind] != NULL) { 996c5e63c71SPaul Luse cmd_chosen++; 9978fbbfccdSPaul Luse cli_context->action = CLI_FILL; 9988fbbfccdSPaul Luse cli_context->blobid = atoll(optarg); 999e5044f79SPaul Luse cli_context->fill_value = atoi(argv[optind]); 1000cf0d7736SPaul Luse } else { 1001cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 1002cf0d7736SPaul Luse } 10038fbbfccdSPaul Luse break; 1004c5e63c71SPaul Luse case 'h': 1005c5e63c71SPaul Luse cmd_chosen++; 1006c5e63c71SPaul Luse cli_context->action = CLI_HELP; 1007c5e63c71SPaul Luse break; 10088fbbfccdSPaul Luse case 'i': 1009e5044f79SPaul Luse if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 1010e5044f79SPaul Luse printf("Your entire blobstore will be destroyed. Are you sure? (y/n) "); 1011c5e63c71SPaul Luse if (scanf("%c%*c", &resp)) { 1012c5e63c71SPaul Luse if (resp == 'y' || resp == 'Y') { 1013c5e63c71SPaul Luse cmd_chosen++; 10148fbbfccdSPaul Luse cli_context->action = CLI_INIT_BS; 1015c5e63c71SPaul Luse } else { 1016c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD) { 1017e5044f79SPaul Luse spdk_app_stop(0); 1018e5044f79SPaul Luse return false; 1019c5e63c71SPaul Luse } 1020c5e63c71SPaul Luse } 1021c5e63c71SPaul Luse } 1022e5044f79SPaul Luse } else { 1023e5044f79SPaul Luse cmd_chosen++; 1024e5044f79SPaul Luse cli_context->action = CLI_INIT_BS; 1025e5044f79SPaul Luse } 10268fbbfccdSPaul Luse break; 10278fbbfccdSPaul Luse case 'r': 1028cf0d7736SPaul Luse if (argv[optind] != NULL) { 1029c5e63c71SPaul Luse cmd_chosen++; 10308fbbfccdSPaul Luse cli_context->action = CLI_REM_XATTR; 10318fbbfccdSPaul Luse cli_context->blobid = atoll(optarg); 1032e5044f79SPaul Luse snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1033cf0d7736SPaul Luse } else { 1034cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 1035cf0d7736SPaul Luse } 10368fbbfccdSPaul Luse break; 10378fbbfccdSPaul Luse case 'l': 10388fbbfccdSPaul Luse if (strcmp("bdevs", optarg) == 0) { 1039c5e63c71SPaul Luse cmd_chosen++; 10408fbbfccdSPaul Luse cli_context->action = CLI_LIST_BDEVS; 10418fbbfccdSPaul Luse } else if (strcmp("blobs", optarg) == 0) { 1042c5e63c71SPaul Luse cmd_chosen++; 10438fbbfccdSPaul Luse cli_context->action = CLI_LIST_BLOBS; 10448fbbfccdSPaul Luse } else { 1045e5044f79SPaul Luse usage(cli_context, "ERROR: invalid option for list\n"); 10468fbbfccdSPaul Luse } 10478fbbfccdSPaul Luse break; 10488fbbfccdSPaul Luse case 'm': 1049cf0d7736SPaul Luse if (argv[optind] != NULL) { 1050c5e63c71SPaul Luse cmd_chosen++; 10514952f254SDaniel Verkamp cli_context->action = CLI_IMPORT_BLOB; 10528fbbfccdSPaul Luse cli_context->blobid = atoll(optarg); 1053e5044f79SPaul Luse snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1054cf0d7736SPaul Luse } else { 1055cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 1056cf0d7736SPaul Luse } 10578fbbfccdSPaul Luse break; 10588fbbfccdSPaul Luse case 'n': 10598fbbfccdSPaul Luse cli_context->num_clusters = atoi(optarg); 10608fbbfccdSPaul Luse if (cli_context->num_clusters > 0) { 1061cf0d7736SPaul Luse cmd_chosen++; 10628fbbfccdSPaul Luse cli_context->action = CLI_CREATE_BLOB; 10638fbbfccdSPaul Luse } else { 1064e5044f79SPaul Luse usage(cli_context, "ERROR: invalid option for new\n"); 10658fbbfccdSPaul Luse } 10668fbbfccdSPaul Luse break; 10678fbbfccdSPaul Luse case 'p': 1068c5e63c71SPaul Luse cmd_chosen++; 10698fbbfccdSPaul Luse cli_context->action = CLI_SET_SUPER; 10708fbbfccdSPaul Luse cli_context->superid = atoll(optarg); 10718fbbfccdSPaul Luse break; 1072c5e63c71SPaul Luse case 'S': 1073c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD) { 1074cf0d7736SPaul Luse cmd_chosen++; 1075c5e63c71SPaul Luse cli_context->cli_mode = CLI_MODE_SHELL; 1076c5e63c71SPaul Luse } 1077c5e63c71SPaul Luse cli_context->action = CLI_NONE; 1078c5e63c71SPaul Luse break; 10798fbbfccdSPaul Luse case 's': 1080c5e63c71SPaul Luse cmd_chosen++; 10818fbbfccdSPaul Luse if (strcmp("bs", optarg) == 0) { 10828fbbfccdSPaul Luse cli_context->action = CLI_SHOW_BS; 10838fbbfccdSPaul Luse } else { 10848fbbfccdSPaul Luse cli_context->action = CLI_SHOW_BLOB; 10858fbbfccdSPaul Luse cli_context->blobid = atoll(optarg); 10868fbbfccdSPaul Luse } 10878fbbfccdSPaul Luse break; 1088e5044f79SPaul Luse case 'T': 1089e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD) { 1090e5044f79SPaul Luse cmd_chosen++; 1091e5044f79SPaul Luse cli_context->cli_mode = CLI_MODE_SCRIPT; 1092e5044f79SPaul Luse if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) { 1093e5044f79SPaul Luse g_script.ignore_errors = true; 1094e5044f79SPaul Luse } else { 1095e5044f79SPaul Luse g_script.ignore_errors = false; 1096e5044f79SPaul Luse } 1097e5044f79SPaul Luse snprintf(cli_context->script_file, BUFSIZE, "%s", optarg); 1098e5044f79SPaul Luse } else { 1099e5044f79SPaul Luse cli_context->action = CLI_NONE; 1100e5044f79SPaul Luse } 1101e5044f79SPaul Luse break; 1102c5e63c71SPaul Luse case 'X': 1103c5e63c71SPaul Luse cmd_chosen++; 1104c5e63c71SPaul Luse cli_context->action = CLI_SHELL_EXIT; 1105c5e63c71SPaul Luse break; 11068fbbfccdSPaul Luse case 'x': 1107cf0d7736SPaul Luse if (argv[optind] != NULL || argv[optind + 1] != NULL) { 1108c5e63c71SPaul Luse cmd_chosen++; 11098fbbfccdSPaul Luse cli_context->action = CLI_SET_XATTR; 11108fbbfccdSPaul Luse cli_context->blobid = atoll(optarg); 1111e5044f79SPaul Luse snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1112e5044f79SPaul Luse snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]); 1113cf0d7736SPaul Luse } else { 1114cf0d7736SPaul Luse usage(cli_context, "ERROR: missing parameter.\n"); 1115cf0d7736SPaul Luse } 11168fbbfccdSPaul Luse break; 11178fbbfccdSPaul Luse default: 1118e5044f79SPaul Luse usage(cli_context, "ERROR: invalid option\n"); 11198fbbfccdSPaul Luse } 1120db26b96fSPaul Luse /* only one actual command can be done at a time */ 1121db26b96fSPaul Luse if (cmd_chosen > 1) { 1122e5044f79SPaul Luse usage(cli_context, "Error: Please choose only one command\n"); 11238fbbfccdSPaul Luse } 11248fbbfccdSPaul Luse } 11258fbbfccdSPaul Luse 1126c5e63c71SPaul Luse if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) { 1127e5044f79SPaul Luse usage(cli_context, "Error: Please choose a command.\n"); 11288fbbfccdSPaul Luse } 11298fbbfccdSPaul Luse 1130cfa4cae0SPaul Luse /* 1131cfa4cae0SPaul Luse * We don't check the local boolean because in some modes it will have been set 1132cfa4cae0SPaul Luse * on and earlier command. 1133cfa4cae0SPaul Luse */ 1134cfa4cae0SPaul Luse if (strcmp(cli_context->bdev_name, "") == 0) { 1135cfa4cae0SPaul Luse usage(cli_context, "Error: -b option is required.\n"); 1136cfa4cae0SPaul Luse cmd_chosen = 0; 1137cfa4cae0SPaul Luse } 1138cfa4cae0SPaul Luse 1139c5e63c71SPaul Luse /* in shell mode we'll call getopt multiple times so need to reset its index */ 1140c5e63c71SPaul Luse optind = 0; 1141db26b96fSPaul Luse return (cmd_chosen == 1); 1142c5e63c71SPaul Luse } 1143c5e63c71SPaul Luse 1144c5e63c71SPaul Luse /* 1145e5044f79SPaul Luse * In script mode, we parsed a script file at startup and saved off a bunch of cmd 1146e5044f79SPaul Luse * lines that we now parse with each run of cli_start so we us the same cmd parser 1147e5044f79SPaul Luse * as cmd and shell modes. 1148e5044f79SPaul Luse */ 1149e5044f79SPaul Luse static bool 1150e5044f79SPaul Luse line_parser(struct cli_context_t *cli_context) 1151e5044f79SPaul Luse { 1152e5044f79SPaul Luse bool cmd_chosen; 1153e5044f79SPaul Luse char *tok = NULL; 1154e5044f79SPaul Luse int blob_num = 0; 1155e5044f79SPaul Luse int start_idx = cli_context->argc; 1156e5044f79SPaul Luse int i; 1157e5044f79SPaul Luse 1158e5044f79SPaul Luse printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]); 1159e5044f79SPaul Luse tok = strtok(g_script.cmdline[g_script.cmdline_idx], " "); 1160e5044f79SPaul Luse while (tok != NULL) { 1161e5044f79SPaul Luse /* 1162e5044f79SPaul Luse * We support one replaceable token right now, a $Bn 1163e5044f79SPaul Luse * represents the blobid that was created in position n 1164e5044f79SPaul Luse * so fish this out now and use it here. 1165e5044f79SPaul Luse */ 1166e5044f79SPaul Luse cli_context->argv[cli_context->argc] = strdup(tok); 1167e5044f79SPaul Luse if (tok[0] == '$' && tok[1] == 'B') { 1168e5044f79SPaul Luse tok += 2; 1169e5044f79SPaul Luse blob_num = atoi(tok); 11705589fc4aSPaul Luse if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) { 1171e5044f79SPaul Luse cli_context->argv[cli_context->argc] = 1172e5044f79SPaul Luse realloc(cli_context->argv[cli_context->argc], BUFSIZE); 1173e5044f79SPaul Luse if (cli_context->argv[cli_context->argc] == NULL) { 1174e5044f79SPaul Luse printf("ERROR: unable to realloc memory\n"); 1175e5044f79SPaul Luse spdk_app_stop(-1); 1176e5044f79SPaul Luse } 1177e5044f79SPaul Luse if (g_script.blobid[blob_num] == 0) { 1178e5044f79SPaul Luse printf("ERROR: There is no blob for $B%d\n", 1179e5044f79SPaul Luse blob_num); 1180e5044f79SPaul Luse } 1181e5044f79SPaul Luse snprintf(cli_context->argv[cli_context->argc], BUFSIZE, 1182e5044f79SPaul Luse "%" PRIu64, g_script.blobid[blob_num]); 11835589fc4aSPaul Luse } else { 11845589fc4aSPaul Luse printf("ERROR: Invalid token or exceeded max blobs of %d\n", 11855589fc4aSPaul Luse MAX_SCRIPT_BLOBS); 11865589fc4aSPaul Luse } 1187e5044f79SPaul Luse } 1188e5044f79SPaul Luse cli_context->argc++; 1189e5044f79SPaul Luse tok = strtok(NULL, " "); 1190e5044f79SPaul Luse } 1191e5044f79SPaul Luse 1192e5044f79SPaul Luse /* call parse cmd line with user input as args */ 1193e5044f79SPaul Luse cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1194e5044f79SPaul Luse 1195e5044f79SPaul Luse /* free strdup memory and reset arg count for next shell interaction */ 1196e5044f79SPaul Luse for (i = start_idx; i < cli_context->argc; i++) { 1197e5044f79SPaul Luse free(cli_context->argv[i]); 1198e5044f79SPaul Luse cli_context->argv[i] = NULL; 1199e5044f79SPaul Luse } 1200e5044f79SPaul Luse cli_context->argc = 1; 1201e5044f79SPaul Luse 1202e5044f79SPaul Luse g_script.cmdline_idx++; 1203e5044f79SPaul Luse assert(g_script.cmdline_idx < MAX_SCRIPT_LINES); 1204e5044f79SPaul Luse 1205e5044f79SPaul Luse if (cmd_chosen == false) { 1206e5044f79SPaul Luse printf("ERROR: Invalid script line starting with: %s\n\n", 1207e5044f79SPaul Luse g_script.cmdline[g_script.cmdline_idx - 1]); 1208e5044f79SPaul Luse if (g_script.ignore_errors == false) { 1209e5044f79SPaul Luse printf("** Aborting **\n"); 1210e5044f79SPaul Luse cli_context->action = CLI_SHELL_EXIT; 1211e5044f79SPaul Luse cmd_chosen = true; 1212e5044f79SPaul Luse unload_bs(cli_context, "", 0); 1213e5044f79SPaul Luse } else { 1214e5044f79SPaul Luse printf("** Skipping **\n"); 1215e5044f79SPaul Luse } 1216e5044f79SPaul Luse } 1217e5044f79SPaul Luse 1218e5044f79SPaul Luse return cmd_chosen; 1219e5044f79SPaul Luse } 1220e5044f79SPaul Luse 1221e5044f79SPaul Luse /* 1222e5044f79SPaul Luse * For script mode, we read a series of commands from a text file and store them 1223e5044f79SPaul Luse * in a global struct. That, along with the cli_mode that tells us we're in 1224e5044f79SPaul Luse * script mode is what feeds the rest of the app in the same way as is it were 1225e5044f79SPaul Luse * getting commands from shell mode. 1226e5044f79SPaul Luse */ 1227e5044f79SPaul Luse static void 1228e5044f79SPaul Luse parse_script(struct cli_context_t *cli_context) 1229e5044f79SPaul Luse { 1230e5044f79SPaul Luse FILE *fp = NULL; 1231e5044f79SPaul Luse size_t bufsize = BUFSIZE; 1232e5044f79SPaul Luse int64_t bytes_in = 0; 1233e5044f79SPaul Luse int i = 0; 1234e5044f79SPaul Luse 1235e5044f79SPaul Luse /* initialize global script values */ 1236e5044f79SPaul Luse for (i = 0; i < MAX_SCRIPT_BLOBS; i++) { 1237e5044f79SPaul Luse g_script.blobid[i] = 0; 1238e5044f79SPaul Luse } 1239e5044f79SPaul Luse g_script.blobid_idx = 0; 1240e5044f79SPaul Luse g_script.cmdline_idx = 0; 1241e5044f79SPaul Luse i = 0; 1242e5044f79SPaul Luse 1243e5044f79SPaul Luse fp = fopen(cli_context->script_file, "r"); 1244e5044f79SPaul Luse if (fp == NULL) { 1245e5044f79SPaul Luse printf("ERROR: unable to open script: %s\n", 1246e5044f79SPaul Luse cli_context->script_file); 1247e5044f79SPaul Luse cli_cleanup(cli_context); 1248e5044f79SPaul Luse exit(-1); 1249e5044f79SPaul Luse } 1250e5044f79SPaul Luse 1251e5044f79SPaul Luse do { 1252e5044f79SPaul Luse bytes_in = getline(&g_script.cmdline[i], &bufsize, fp); 1253e5044f79SPaul Luse if (bytes_in > 0) { 1254e5044f79SPaul Luse /* replace newline with null */ 1255e5044f79SPaul Luse spdk_str_chomp(g_script.cmdline[i]); 1256e5044f79SPaul Luse 1257e5044f79SPaul Luse /* ignore comments */ 1258e5044f79SPaul Luse if (g_script.cmdline[i][0] != '#') { 1259e5044f79SPaul Luse i++; 1260e5044f79SPaul Luse } 1261e5044f79SPaul Luse } 12625589fc4aSPaul Luse } while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1); 1263e5044f79SPaul Luse fclose(fp); 1264e5044f79SPaul Luse 1265e5044f79SPaul Luse /* add an exit cmd in case they didn't */ 1266e5044f79SPaul Luse g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE); 1267e5044f79SPaul Luse if (g_script.cmdline[i] == NULL) { 1268e5044f79SPaul Luse int j; 1269e5044f79SPaul Luse 1270e5044f79SPaul Luse for (j = 0; j < i; j++) { 1271e5044f79SPaul Luse free(g_script.cmdline[j]); 1272e5044f79SPaul Luse g_script.cmdline[j] = NULL; 1273e5044f79SPaul Luse } 1274e5044f79SPaul Luse unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0); 1275e5044f79SPaul Luse } 1276e5044f79SPaul Luse snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X"); 1277e5044f79SPaul Luse g_script.max_index = i; 1278e5044f79SPaul Luse } 1279e5044f79SPaul Luse 1280e5044f79SPaul Luse /* 1281c5e63c71SPaul Luse * Provides for a shell interface as opposed to one shot command line. 1282c5e63c71SPaul Luse */ 1283c5e63c71SPaul Luse static bool 1284c5e63c71SPaul Luse cli_shell(void *arg1, void *arg2) 1285c5e63c71SPaul Luse { 1286c5e63c71SPaul Luse struct cli_context_t *cli_context = arg1; 1287c5e63c71SPaul Luse char *line = NULL; 1288c5e63c71SPaul Luse ssize_t buf_size = 0; 1289c5e63c71SPaul Luse ssize_t bytes_in = 0; 1290c5e63c71SPaul Luse ssize_t tok_len = 0; 1291c5e63c71SPaul Luse char *tok = NULL; 1292c5e63c71SPaul Luse bool cmd_chosen = false; 1293c5e63c71SPaul Luse int start_idx = cli_context->argc; 1294c5e63c71SPaul Luse int i; 1295c5e63c71SPaul Luse 1296c5e63c71SPaul Luse printf("blob> "); 1297c5e63c71SPaul Luse bytes_in = getline(&line, &buf_size, stdin); 1298c5e63c71SPaul Luse 1299168bc9f1SDaniel Verkamp /* If getline() failed (EOF), exit the shell. */ 1300168bc9f1SDaniel Verkamp if (bytes_in < 0) { 1301168bc9f1SDaniel Verkamp free(line); 1302168bc9f1SDaniel Verkamp cli_context->action = CLI_SHELL_EXIT; 1303168bc9f1SDaniel Verkamp return true; 1304168bc9f1SDaniel Verkamp } 1305168bc9f1SDaniel Verkamp 1306c5e63c71SPaul Luse /* parse input and update cli_context so we can use common option parser */ 1307c5e63c71SPaul Luse if (bytes_in > 0) { 1308c5e63c71SPaul Luse tok = strtok(line, " "); 1309c5e63c71SPaul Luse } 1310c5e63c71SPaul Luse while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) { 1311c5e63c71SPaul Luse cli_context->argv[cli_context->argc] = strdup(tok); 1312c5e63c71SPaul Luse tok_len = strlen(tok); 1313c5e63c71SPaul Luse cli_context->argc++; 131498a2ec3eSPaul Luse tok = strtok(NULL, " "); 1315c5e63c71SPaul Luse } 1316c5e63c71SPaul Luse 1317c5e63c71SPaul Luse /* replace newline on last arg with null */ 1318c5e63c71SPaul Luse if (tok_len) { 1319c5e63c71SPaul Luse spdk_str_chomp(cli_context->argv[cli_context->argc - 1]); 1320c5e63c71SPaul Luse } 1321c5e63c71SPaul Luse 1322c5e63c71SPaul Luse /* call parse cmd line with user input as args */ 1323c5e63c71SPaul Luse cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1324c5e63c71SPaul Luse 1325c5e63c71SPaul Luse /* free strdup mem & reset arg count for next shell interaction */ 1326c5e63c71SPaul Luse for (i = start_idx; i < cli_context->argc; i++) { 1327c5e63c71SPaul Luse free(cli_context->argv[i]); 1328d4e565b4SPaul Luse cli_context->argv[i] = NULL; 1329c5e63c71SPaul Luse } 1330c5e63c71SPaul Luse cli_context->argc = 1; 1331c5e63c71SPaul Luse 1332c5e63c71SPaul Luse free(line); 1333c5e63c71SPaul Luse 1334c5e63c71SPaul Luse return cmd_chosen; 1335c5e63c71SPaul Luse } 1336c5e63c71SPaul Luse 1337866f15b3SPaul Luse /* 1338866f15b3SPaul Luse * This is the function we pass into the SPDK framework that gets 1339866f15b3SPaul Luse * called first. 1340866f15b3SPaul Luse */ 1341866f15b3SPaul Luse static void 1342866f15b3SPaul Luse cli_start(void *arg1, void *arg2) 1343866f15b3SPaul Luse { 1344866f15b3SPaul Luse struct cli_context_t *cli_context = arg1; 1345866f15b3SPaul Luse 1346866f15b3SPaul Luse /* 1347e5044f79SPaul Luse * If we're in script mode, we already have a list of commands so 1348e5044f79SPaul Luse * just need to pull them out one at a time and process them. 1349e5044f79SPaul Luse */ 1350e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1351e5044f79SPaul Luse while (line_parser(cli_context) == false); 1352e5044f79SPaul Luse } 1353e5044f79SPaul Luse 1354e5044f79SPaul Luse /* 1355866f15b3SPaul Luse * The initial cmd line options are parsed once before this function is 1356866f15b3SPaul Luse * called so if there is no action, we're in shell mode and will loop 1357866f15b3SPaul Luse * here until a a valid option is parsed and returned. 1358866f15b3SPaul Luse */ 1359866f15b3SPaul Luse if (cli_context->action == CLI_NONE) { 1360866f15b3SPaul Luse while (cli_shell(cli_context, NULL) == false); 1361866f15b3SPaul Luse } 1362866f15b3SPaul Luse 1363866f15b3SPaul Luse /* Decide what to do next based on cmd line parsing. */ 1364866f15b3SPaul Luse switch (cli_context->action) { 1365866f15b3SPaul Luse case CLI_SET_SUPER: 1366866f15b3SPaul Luse case CLI_SHOW_BS: 1367866f15b3SPaul Luse case CLI_CREATE_BLOB: 1368866f15b3SPaul Luse case CLI_SET_XATTR: 1369866f15b3SPaul Luse case CLI_REM_XATTR: 1370866f15b3SPaul Luse case CLI_SHOW_BLOB: 1371866f15b3SPaul Luse case CLI_LIST_BLOBS: 13724952f254SDaniel Verkamp case CLI_DUMP_BLOB: 13734952f254SDaniel Verkamp case CLI_IMPORT_BLOB: 1374866f15b3SPaul Luse case CLI_FILL: 1375866f15b3SPaul Luse load_bs(cli_context); 1376866f15b3SPaul Luse break; 1377866f15b3SPaul Luse case CLI_INIT_BS: 1378866f15b3SPaul Luse init_bs(cli_context); 1379866f15b3SPaul Luse break; 1380866f15b3SPaul Luse case CLI_LIST_BDEVS: 1381866f15b3SPaul Luse list_bdevs(cli_context); 1382866f15b3SPaul Luse break; 1383866f15b3SPaul Luse case CLI_SHELL_EXIT: 1384866f15b3SPaul Luse /* 1385866f15b3SPaul Luse * Because shell mode reuses cmd mode functions, the blobstore 1386866f15b3SPaul Luse * is loaded/unloaded with every action so we just need to 1387866f15b3SPaul Luse * stop the framework. For this app there's no need to optimize 1388866f15b3SPaul Luse * and keep the blobstore open while the app is in shell mode. 1389866f15b3SPaul Luse */ 1390866f15b3SPaul Luse spdk_app_stop(0); 1391866f15b3SPaul Luse break; 1392866f15b3SPaul Luse case CLI_HELP: 1393e5044f79SPaul Luse usage(cli_context, ""); 1394866f15b3SPaul Luse unload_complete(cli_context, 0); 1395866f15b3SPaul Luse break; 1396866f15b3SPaul Luse default: 1397866f15b3SPaul Luse /* should never get here */ 139898a2ec3eSPaul Luse exit(-1); 1399866f15b3SPaul Luse break; 1400866f15b3SPaul Luse } 1401866f15b3SPaul Luse } 1402c5e63c71SPaul Luse 1403c5e63c71SPaul Luse int 1404c5e63c71SPaul Luse main(int argc, char **argv) 1405c5e63c71SPaul Luse { 1406c5e63c71SPaul Luse struct spdk_app_opts opts = {}; 1407c5e63c71SPaul Luse struct cli_context_t *cli_context = NULL; 1408e5044f79SPaul Luse bool cmd_chosen; 1409c5e63c71SPaul Luse int rc = 0; 1410c5e63c71SPaul Luse 1411c5e63c71SPaul Luse if (argc < 2) { 1412e5044f79SPaul Luse usage(cli_context, "ERROR: Invalid option\n"); 1413e5044f79SPaul Luse exit(-1); 1414c5e63c71SPaul Luse } 1415c5e63c71SPaul Luse 1416c5e63c71SPaul Luse cli_context = calloc(1, sizeof(struct cli_context_t)); 1417c5e63c71SPaul Luse if (cli_context == NULL) { 1418c5e63c71SPaul Luse printf("ERROR: could not allocate context structure\n"); 1419c5e63c71SPaul Luse exit(-1); 1420c5e63c71SPaul Luse } 1421cfa4cae0SPaul Luse 1422c5e63c71SPaul Luse /* default to CMD mode until we've parsed the first parms */ 1423c5e63c71SPaul Luse cli_context->cli_mode = CLI_MODE_CMD; 1424c5e63c71SPaul Luse cli_context->argv[0] = strdup(argv[0]); 1425c5e63c71SPaul Luse cli_context->argc = 1; 1426c5e63c71SPaul Luse 1427c5e63c71SPaul Luse /* parse command line */ 1428e5044f79SPaul Luse cmd_chosen = cmd_parser(argc, argv, cli_context); 1429c5e63c71SPaul Luse free(cli_context->argv[0]); 1430e5044f79SPaul Luse cli_context->argv[0] = NULL; 1431e5044f79SPaul Luse if (cmd_chosen == false) { 1432e5044f79SPaul Luse cli_cleanup(cli_context); 1433e5044f79SPaul Luse exit(-1); 1434e5044f79SPaul Luse } 1435c5e63c71SPaul Luse 1436c5e63c71SPaul Luse /* after displaying help, just exit */ 1437c5e63c71SPaul Luse if (cli_context->action == CLI_HELP) { 1438e5044f79SPaul Luse usage(cli_context, ""); 1439c5e63c71SPaul Luse cli_cleanup(cli_context); 1440c5e63c71SPaul Luse exit(-1); 1441c5e63c71SPaul Luse } 1442c5e63c71SPaul Luse 1443c5e63c71SPaul Luse /* if they don't supply a conf name, use the default */ 1444c5e63c71SPaul Luse if (!cli_context->config_file) { 1445c5e63c71SPaul Luse cli_context->config_file = program_conf; 1446c5e63c71SPaul Luse } 1447c5e63c71SPaul Luse 1448c5e63c71SPaul Luse /* if the config file doesn't exist, tell them how to make one */ 1449c5e63c71SPaul Luse if (access(cli_context->config_file, F_OK) == -1) { 1450c5e63c71SPaul Luse printf("Error: No config file found.\n"); 1451c5e63c71SPaul Luse printf("To create a config file named 'blobcli.conf' for your NVMe device:\n"); 1452c5e63c71SPaul Luse printf(" <path to spdk>/scripts/gen_nvme.sh > blobcli.conf\n"); 1453c5e63c71SPaul Luse printf("and then re-run the cli tool.\n"); 145498a2ec3eSPaul Luse exit(-1); 1455c5e63c71SPaul Luse } 1456c5e63c71SPaul Luse 1457e5044f79SPaul Luse /* 1458e5044f79SPaul Luse * For script mode we keep a bunch of stuff in a global since 1459e5044f79SPaul Luse * none if it is passed back and forth to SPDK. 1460e5044f79SPaul Luse */ 1461e5044f79SPaul Luse if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1462e5044f79SPaul Luse /* 1463e5044f79SPaul Luse * Now we'll build up the global which will direct this run of the app 1464e5044f79SPaul Luse * as it will have a list (g_script) of all of the commands line by 1465e5044f79SPaul Luse * line as if they were typed in on the shell at cmd line. 1466e5044f79SPaul Luse */ 1467e5044f79SPaul Luse parse_script(cli_context); 1468e5044f79SPaul Luse } 1469e5044f79SPaul Luse 14708fbbfccdSPaul Luse /* Set default values in opts struct along with name and conf file. */ 14718fbbfccdSPaul Luse spdk_app_opts_init(&opts); 14728fbbfccdSPaul Luse opts.name = "blobcli"; 1473c5e63c71SPaul Luse opts.config_file = cli_context->config_file; 14748fbbfccdSPaul Luse 1475c5e63c71SPaul Luse cli_context->app_started = true; 14768fbbfccdSPaul Luse rc = spdk_app_start(&opts, cli_start, cli_context, NULL); 14778fbbfccdSPaul Luse if (rc) { 14788fbbfccdSPaul Luse printf("ERROR!\n"); 14798fbbfccdSPaul Luse } 14808fbbfccdSPaul Luse 14818fbbfccdSPaul Luse /* Free up memory that we allocated */ 14828fbbfccdSPaul Luse cli_cleanup(cli_context); 14838fbbfccdSPaul Luse 14848fbbfccdSPaul Luse /* Gracefully close out all of the SPDK subsystems. */ 14858fbbfccdSPaul Luse spdk_app_fini(); 14868fbbfccdSPaul Luse return rc; 14878fbbfccdSPaul Luse } 1488