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