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