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_BLOB, 72 CLI_DUMP_BLOB, 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 if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 254 spdk_app_stop(bserrno); 255 256 } 257 } 258 259 /* 260 * Callback for closing a blob. 261 */ 262 static void 263 close_cb(void *arg1, int bserrno) 264 { 265 struct cli_context_t *cli_context = arg1; 266 267 if (bserrno) { 268 unload_bs(cli_context, "Error in close callback", 269 bserrno); 270 return; 271 } 272 unload_bs(cli_context, "", 0); 273 } 274 275 /* 276 * Callback function for sync'ing metadata. 277 */ 278 static void 279 sync_cb(void *arg1, int bserrno) 280 { 281 struct cli_context_t *cli_context = arg1; 282 283 if (bserrno) { 284 unload_bs(cli_context, "Error in sync callback", 285 bserrno); 286 return; 287 } 288 289 spdk_bs_md_close_blob(&cli_context->blob, close_cb, 290 cli_context); 291 } 292 293 /* 294 * Callback function for opening a blob after creating. 295 */ 296 static void 297 open_now_resize_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 298 { 299 struct cli_context_t *cli_context = cb_arg; 300 int rc = 0; 301 uint64_t total = 0; 302 303 if (bserrno) { 304 unload_bs(cli_context, "Error in open completion", 305 bserrno); 306 return; 307 } 308 cli_context->blob = blob; 309 310 rc = spdk_bs_md_resize_blob(cli_context->blob, 311 cli_context->num_clusters); 312 if (rc) { 313 unload_bs(cli_context, "Error in blob resize", 314 bserrno); 315 return; 316 } 317 318 total = spdk_blob_get_num_clusters(cli_context->blob); 319 printf("blob now has USED clusters of %" PRIu64 "\n", 320 total); 321 322 /* 323 * Always a good idea to sync after MD changes or the changes 324 * may be lost if things aren't closed cleanly. 325 */ 326 spdk_bs_md_sync_blob(cli_context->blob, sync_cb, 327 cli_context); 328 } 329 330 /* 331 * Callback function for creating a blob. 332 */ 333 static void 334 blob_create_cb(void *arg1, spdk_blob_id blobid, int bserrno) 335 { 336 struct cli_context_t *cli_context = arg1; 337 338 if (bserrno) { 339 unload_bs(cli_context, "Error in blob create callback", 340 bserrno); 341 return; 342 } 343 344 cli_context->blobid = blobid; 345 printf("New blob id %" PRIu64 "\n", cli_context->blobid); 346 347 /* if we're in script mode, we need info on all blobids for later */ 348 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 349 g_script.blobid[g_script.blobid_idx++] = blobid; 350 } 351 352 /* We have to open the blob before we can do things like resize. */ 353 spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid, 354 open_now_resize_cb, cli_context); 355 } 356 357 /* 358 * Callback for get_super where we'll continue on to show blobstore info. 359 */ 360 static void 361 show_bs_cb(void *arg1, spdk_blob_id blobid, int bserrno) 362 { 363 struct cli_context_t *cli_context = arg1; 364 struct spdk_bs_type bstype; 365 uint64_t val; 366 struct spdk_bdev *bdev = NULL; 367 368 if (bserrno && bserrno != -ENOENT) { 369 unload_bs(cli_context, "Error in get_super callback", 370 bserrno); 371 return; 372 } 373 cli_context->superid = blobid; 374 375 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 376 if (bdev == NULL) { 377 unload_bs(cli_context, "Error w/bdev in get_super callback", 378 bserrno); 379 return; 380 } 381 382 printf("Blobstore Public Info:\n"); 383 printf("\tUsing bdev Product Name: %s\n", 384 spdk_bdev_get_product_name(bdev)); 385 printf("\tAPI Version: %d\n", SPDK_BS_VERSION); 386 387 if (bserrno != -ENOENT) { 388 printf("\tsuper blob ID: %" PRIu64 "\n", cli_context->superid); 389 } else { 390 printf("\tsuper blob ID: none assigned\n"); 391 } 392 393 printf("\tpage size: %" PRIu64 "\n", cli_context->page_size); 394 395 val = spdk_bs_get_cluster_size(cli_context->bs); 396 printf("\tcluster size: %" PRIu64 "\n", val); 397 398 val = spdk_bs_free_cluster_count(cli_context->bs); 399 printf("\t# free clusters: %" PRIu64 "\n", val); 400 401 bstype = spdk_bs_get_bstype(cli_context->bs); 402 spdk_trace_dump(stdout, "\tblobstore type:", &bstype, sizeof(bstype)); 403 404 /* 405 * Private info isn't accessible via the public API but 406 * may be useful for debug of blobstore based applications. 407 */ 408 printf("\nBlobstore Private Info:\n"); 409 printf("\tMetadata start (pages): %" PRIu64 "\n", 410 cli_context->bs->md_start); 411 printf("\tMetadata length (pages): %d \n", 412 cli_context->bs->md_len); 413 414 unload_bs(cli_context, "", 0); 415 } 416 417 /* 418 * Show detailed info about a particular blob. 419 */ 420 static void 421 show_blob(struct cli_context_t *cli_context) 422 { 423 uint64_t val; 424 struct spdk_xattr_names *names; 425 const void *value; 426 size_t value_len; 427 char data[BUFSIZE]; 428 unsigned int i; 429 430 printf("Blob Public Info:\n"); 431 432 printf("blob ID: %" PRIu64 "\n", cli_context->blobid); 433 434 val = spdk_blob_get_num_clusters(cli_context->blob); 435 printf("# of clusters: %" PRIu64 "\n", val); 436 437 printf("# of bytes: %" PRIu64 "\n", 438 val * spdk_bs_get_cluster_size(cli_context->bs)); 439 440 val = spdk_blob_get_num_pages(cli_context->blob); 441 printf("# of pages: %" PRIu64 "\n", val); 442 443 spdk_bs_md_get_xattr_names(cli_context->blob, &names); 444 445 printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names)); 446 printf("xattrs:\n"); 447 for (i = 0; i < spdk_xattr_names_get_count(names); i++) { 448 spdk_bs_md_get_xattr_value(cli_context->blob, 449 spdk_xattr_names_get_name(names, i), 450 &value, &value_len); 451 if ((value_len + 1) > sizeof(data)) { 452 printf("FYI: adjusting size of xattr due to CLI limits.\n"); 453 value_len = sizeof(data) - 1; 454 } 455 memcpy(&data, value, value_len); 456 data[value_len] = '\0'; 457 printf("\n(%d) Name:%s\n", i, 458 spdk_xattr_names_get_name(names, i)); 459 printf("(%d) Value:\n", i); 460 spdk_trace_dump(stdout, "", value, value_len); 461 } 462 463 /* 464 * Private info isn't accessible via the public API but 465 * may be useful for debug of blobstore based applications. 466 */ 467 printf("\nBlob Private Info:\n"); 468 switch (cli_context->blob->state) { 469 case SPDK_BLOB_STATE_DIRTY: 470 printf("state: DIRTY\n"); 471 break; 472 case SPDK_BLOB_STATE_CLEAN: 473 printf("state: CLEAN\n"); 474 break; 475 case SPDK_BLOB_STATE_LOADING: 476 printf("state: LOADING\n"); 477 break; 478 case SPDK_BLOB_STATE_SYNCING: 479 printf("state: SYNCING\n"); 480 break; 481 default: 482 printf("state: UNKNOWN\n"); 483 break; 484 } 485 printf("open ref count: %d\n", 486 cli_context->blob->open_ref); 487 488 spdk_xattr_names_free(names); 489 } 490 491 /* 492 * Callback for getting the first blob, shared with simple blob listing as well. 493 */ 494 static void 495 blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno) 496 { 497 struct cli_context_t *cli_context = arg1; 498 499 if (bserrno) { 500 if (bserrno == -ENOENT) { 501 /* this simply means there are no more blobs */ 502 unload_bs(cli_context, "", 0); 503 } else { 504 unload_bs(cli_context, "Error in blob iter callback", 505 bserrno); 506 } 507 return; 508 } 509 510 if (cli_context->action == CLI_LIST_BLOBS) { 511 printf("\nList BLOBS:\n"); 512 printf("Found blob with ID# %" PRIu64 "\n", 513 spdk_blob_get_id(blob)); 514 } else if (spdk_blob_get_id(blob) == cli_context->blobid) { 515 /* 516 * Found the blob we're looking for, but we need to finish 517 * iterating even after showing the info so that internally 518 * the blobstore logic will close the blob. Or we could 519 * chose to close it now, either way. 520 */ 521 cli_context->blob = blob; 522 show_blob(cli_context); 523 } 524 525 spdk_bs_md_iter_next(cli_context->bs, &blob, blob_iter_cb, 526 cli_context); 527 } 528 529 /* 530 * Callback for setting the super blob ID. 531 */ 532 static void 533 set_super_cb(void *arg1, int bserrno) 534 { 535 struct cli_context_t *cli_context = arg1; 536 537 if (bserrno) { 538 unload_bs(cli_context, "Error in set_super callback", 539 bserrno); 540 return; 541 } 542 543 printf("Super Blob ID has been set.\n"); 544 unload_bs(cli_context, "", 0); 545 } 546 547 /* 548 * Callback for set_xattr_open where we set or delete xattrs. 549 */ 550 static void 551 set_xattr_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 552 { 553 struct cli_context_t *cli_context = cb_arg; 554 555 if (bserrno) { 556 unload_bs(cli_context, "Error in blob open callback", 557 bserrno); 558 return; 559 } 560 cli_context->blob = blob; 561 562 if (cli_context->action == CLI_SET_XATTR) { 563 spdk_blob_md_set_xattr(cli_context->blob, 564 cli_context->key, 565 cli_context->value, 566 strlen(cli_context->value) + 1); 567 printf("Xattr has been set.\n"); 568 } else { 569 spdk_blob_md_remove_xattr(cli_context->blob, 570 cli_context->key); 571 printf("Xattr has been removed.\n"); 572 } 573 574 spdk_bs_md_sync_blob(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_PAGES, cli_context->page_size, 594 cli_context->fp); 595 if (bytes_written != cli_context->page_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->page_count < cli_context->blob_pages) { 604 /* perform another read */ 605 spdk_bs_io_read_blob(cli_context->blob, cli_context->channel, 606 cli_context->buff, cli_context->page_count, 607 NUM_PAGES, 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_bs_md_close_blob(&cli_context->blob, close_cb, 613 cli_context); 614 } 615 } 616 617 /* 618 * Callback for write completion on the import of a file to a blob. 619 */ 620 static void 621 write_imp_cb(void *arg1, int bserrno) 622 { 623 struct cli_context_t *cli_context = arg1; 624 uint64_t bytes_read; 625 626 if (bserrno) { 627 fclose(cli_context->fp); 628 unload_bs(cli_context, "Error in write completion", 629 bserrno); 630 return; 631 } 632 633 if (cli_context->bytes_so_far < cli_context->filesize) { 634 /* perform another file read */ 635 bytes_read = fread(cli_context->buff, 1, 636 cli_context->page_size, 637 cli_context->fp); 638 cli_context->bytes_so_far += bytes_read; 639 640 /* if this read is < 1 page, fill with 0s */ 641 if (bytes_read < cli_context->page_size) { 642 uint8_t *offset = cli_context->buff + bytes_read; 643 memset(offset, 0, cli_context->page_size - bytes_read); 644 } 645 } else { 646 /* 647 * Done reading the file, fill the rest of the blob with 0s, 648 * yeah we're memsetting the same page over and over here 649 */ 650 memset(cli_context->buff, 0, cli_context->page_size); 651 } 652 if (++cli_context->page_count < cli_context->blob_pages) { 653 printf("."); 654 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 655 cli_context->buff, cli_context->page_count, 656 NUM_PAGES, write_imp_cb, cli_context); 657 } else { 658 /* done writing */ 659 printf("\nBlob import complete (from %s).\n", cli_context->file); 660 fclose(cli_context->fp); 661 spdk_bs_md_close_blob(&cli_context->blob, close_cb, cli_context); 662 } 663 } 664 665 /* 666 * Callback for open blobs where we'll continue on dump a blob to a file or 667 * import a file to a blob. For dump, the resulting file will always be the 668 * full size of the blob. For import, the blob will fill with the file 669 * contents first and then 0 out the rest of the blob. 670 */ 671 static void 672 dump_imp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 673 { 674 struct cli_context_t *cli_context = cb_arg; 675 676 if (bserrno) { 677 unload_bs(cli_context, "Error in blob open callback", 678 bserrno); 679 return; 680 } 681 cli_context->blob = blob; 682 683 /* 684 * We'll transfer just one page at a time to keep the buffer 685 * small. This could be bigger of course. 686 */ 687 cli_context->buff = spdk_dma_malloc(cli_context->page_size, 688 ALIGN_4K, NULL); 689 if (cli_context->buff == NULL) { 690 printf("Error in allocating memory\n"); 691 spdk_bs_md_close_blob(&cli_context->blob, close_cb, cli_context); 692 return; 693 } 694 printf("Working"); 695 cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob); 696 cli_context->page_count = 0; 697 if (cli_context->action == CLI_DUMP_BLOB) { 698 cli_context->fp = fopen(cli_context->file, "w"); 699 if (cli_context->fp == NULL) { 700 printf("Error in opening file\n"); 701 spdk_bs_md_close_blob(&cli_context->blob, close_cb, cli_context); 702 return; 703 } 704 705 /* read a page of data from the blob */ 706 spdk_bs_io_read_blob(cli_context->blob, cli_context->channel, 707 cli_context->buff, cli_context->page_count, 708 NUM_PAGES, read_dump_cb, cli_context); 709 } else { 710 cli_context->fp = fopen(cli_context->file, "r"); 711 if (cli_context->fp == NULL) { 712 printf("Error in opening file\n"); 713 spdk_bs_md_close_blob(&cli_context->blob, close_cb, cli_context); 714 return; 715 } 716 717 /* get the filesize then rewind read a page of data from file */ 718 fseek(cli_context->fp, 0L, SEEK_END); 719 cli_context->filesize = ftell(cli_context->fp); 720 rewind(cli_context->fp); 721 cli_context->bytes_so_far = fread(cli_context->buff, NUM_PAGES, 722 cli_context->page_size, 723 cli_context->fp); 724 725 /* if the file is < a page, fill the rest with 0s */ 726 if (cli_context->filesize < cli_context->page_size) { 727 uint8_t *offset = 728 cli_context->buff + cli_context->filesize; 729 730 memset(offset, 0, 731 cli_context->page_size - cli_context->filesize); 732 } 733 734 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 735 cli_context->buff, cli_context->page_count, 736 NUM_PAGES, write_imp_cb, cli_context); 737 } 738 } 739 740 /* 741 * Callback function for writing a specific pattern to page 0. 742 */ 743 static void 744 write_cb(void *arg1, int bserrno) 745 { 746 struct cli_context_t *cli_context = arg1; 747 748 if (bserrno) { 749 unload_bs(cli_context, "Error in write completion", 750 bserrno); 751 return; 752 } 753 printf("."); 754 if (++cli_context->page_count < cli_context->blob_pages) { 755 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 756 cli_context->buff, cli_context->page_count, 757 NUM_PAGES, write_cb, cli_context); 758 } else { 759 /* done writing */ 760 printf("\nBlob fill complete (with 0x%x).\n", cli_context->fill_value); 761 spdk_bs_md_close_blob(&cli_context->blob, close_cb, 762 cli_context); 763 } 764 } 765 766 /* 767 * Callback function to fill a blob with a value, callback from open. 768 */ 769 static void 770 fill_blob_cb(void *arg1, struct spdk_blob *blob, int bserrno) 771 { 772 struct cli_context_t *cli_context = arg1; 773 774 if (bserrno) { 775 unload_bs(cli_context, "Error in open callback", 776 bserrno); 777 return; 778 } 779 780 cli_context->blob = blob; 781 cli_context->page_count = 0; 782 cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob); 783 cli_context->buff = spdk_dma_malloc(cli_context->page_size, 784 ALIGN_4K, NULL); 785 if (cli_context->buff == NULL) { 786 unload_bs(cli_context, "Error in allocating memory", 787 -ENOMEM); 788 return; 789 } 790 791 memset(cli_context->buff, cli_context->fill_value, 792 cli_context->page_size); 793 printf("Working"); 794 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 795 cli_context->buff, 796 STARTING_PAGE, NUM_PAGES, write_cb, cli_context); 797 } 798 799 /* 800 * Multiple actions require us to open the bs first so here we use 801 * a common callback to set a bunch of values and then move on to 802 * the next step saved off via function pointer. 803 */ 804 static void 805 load_bs_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 806 { 807 struct cli_context_t *cli_context = arg1; 808 809 if (bserrno) { 810 unload_bs(cli_context, "Error in load callback", 811 bserrno); 812 return; 813 } 814 815 cli_context->bs = bs; 816 cli_context->page_size = spdk_bs_get_page_size(cli_context->bs); 817 cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs); 818 if (cli_context->channel == NULL) { 819 unload_bs(cli_context, "Error in allocating channel", 820 -ENOMEM); 821 return; 822 } 823 824 switch (cli_context->action) { 825 case CLI_SET_SUPER: 826 spdk_bs_set_super(cli_context->bs, cli_context->superid, 827 set_super_cb, cli_context); 828 break; 829 case CLI_SHOW_BS: 830 spdk_bs_get_super(cli_context->bs, show_bs_cb, cli_context); 831 break; 832 case CLI_CREATE_BLOB: 833 spdk_bs_md_create_blob(cli_context->bs, blob_create_cb, 834 cli_context); 835 break; 836 case CLI_SET_XATTR: 837 case CLI_REM_XATTR: 838 spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid, 839 set_xattr_cb, cli_context); 840 break; 841 case CLI_SHOW_BLOB: 842 case CLI_LIST_BLOBS: 843 spdk_bs_md_iter_first(cli_context->bs, blob_iter_cb, cli_context); 844 845 break; 846 case CLI_DUMP_BLOB: 847 case CLI_IMPORT_BLOB: 848 spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid, 849 dump_imp_open_cb, cli_context); 850 break; 851 case CLI_FILL: 852 spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid, 853 fill_blob_cb, cli_context); 854 break; 855 856 default: 857 /* should never get here */ 858 exit(-1); 859 break; 860 } 861 } 862 863 /* 864 * Load the blobstore. 865 */ 866 static void 867 load_bs(struct cli_context_t *cli_context) 868 { 869 struct spdk_bdev *bdev = NULL; 870 struct spdk_bs_dev *bs_dev = NULL; 871 872 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 873 if (bdev == NULL) { 874 printf("Could not find a bdev\n"); 875 spdk_app_stop(-1); 876 return; 877 } 878 879 bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 880 if (bs_dev == NULL) { 881 printf("Could not create blob bdev!!\n"); 882 spdk_app_stop(-1); 883 return; 884 } 885 886 spdk_bs_load(bs_dev, NULL, load_bs_cb, cli_context); 887 } 888 889 /* 890 * Lists all the blobs on this blobstore. 891 */ 892 static void 893 list_bdevs(struct cli_context_t *cli_context) 894 { 895 struct spdk_bdev *bdev = NULL; 896 897 printf("\nList bdevs:\n"); 898 899 bdev = spdk_bdev_first(); 900 if (bdev == NULL) { 901 printf("Could not find a bdev\n"); 902 } 903 while (bdev) { 904 printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev)); 905 printf("\tbdev Product Name: %s\n", 906 spdk_bdev_get_product_name(bdev)); 907 bdev = spdk_bdev_next(bdev); 908 } 909 910 printf("\n"); 911 if (cli_context->cli_mode == CLI_MODE_CMD) { 912 spdk_app_stop(0); 913 } else { 914 cli_context->action = CLI_NONE; 915 cli_start(cli_context, NULL); 916 } 917 } 918 919 /* 920 * Callback function for initializing a blob. 921 */ 922 static void 923 bs_init_cb(void *cb_arg, struct spdk_blob_store *bs, 924 int bserrno) 925 { 926 struct cli_context_t *cli_context = cb_arg; 927 928 if (bserrno) { 929 unload_bs(cli_context, "Error in bs init callback", 930 bserrno); 931 return; 932 } 933 cli_context->bs = bs; 934 printf("blobstore init'd: (%p)\n", cli_context->bs); 935 936 unload_bs(cli_context, "", 0); 937 } 938 939 /* 940 * Initialize a new blobstore. 941 */ 942 static void 943 init_bs(struct cli_context_t *cli_context) 944 { 945 struct spdk_bdev *bdev = NULL; 946 947 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 948 if (bdev == NULL) { 949 printf("Could not find a bdev\n"); 950 spdk_app_stop(-1); 951 return; 952 } 953 printf("Init blobstore using bdev Product Name: %s\n", 954 spdk_bdev_get_product_name(bdev)); 955 956 cli_context->bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 957 if (cli_context->bs_dev == NULL) { 958 printf("Could not create blob bdev!!\n"); 959 spdk_app_stop(-1); 960 return; 961 } 962 963 spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb, 964 cli_context); 965 } 966 967 /* 968 * Common cmd/option parser for command and shell modes. 969 */ 970 static bool 971 cmd_parser(int argc, char **argv, struct cli_context_t *cli_context) 972 { 973 int op; 974 int cmd_chosen = 0; 975 char resp; 976 977 while ((op = getopt(argc, argv, "b:c:d:f:hil:m:n:p:r:s:ST:Xx:")) != -1) { 978 switch (op) { 979 case 'b': 980 if (strcmp(cli_context->bdev_name, "") == 0) { 981 snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg); 982 } else { 983 printf("Current setting for -b is: %s\n", cli_context->bdev_name); 984 usage(cli_context, "ERROR: -b option can only be set once.\n"); 985 } 986 break; 987 case 'c': 988 if (cli_context->app_started == false) { 989 cli_context->config_file = optarg; 990 } else { 991 usage(cli_context, "ERROR: -c option not valid during shell mode.\n"); 992 } 993 break; 994 case 'd': 995 if (argv[optind] != NULL) { 996 cmd_chosen++; 997 cli_context->action = CLI_DUMP_BLOB; 998 cli_context->blobid = atoll(optarg); 999 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1000 } else { 1001 usage(cli_context, "ERROR: missing parameter.\n"); 1002 } 1003 break; 1004 case 'f': 1005 if (argv[optind] != NULL) { 1006 cmd_chosen++; 1007 cli_context->action = CLI_FILL; 1008 cli_context->blobid = atoll(optarg); 1009 cli_context->fill_value = atoi(argv[optind]); 1010 } else { 1011 usage(cli_context, "ERROR: missing parameter.\n"); 1012 } 1013 break; 1014 case 'h': 1015 cmd_chosen++; 1016 cli_context->action = CLI_HELP; 1017 break; 1018 case 'i': 1019 if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 1020 printf("Your entire blobstore will be destroyed. Are you sure? (y/n) "); 1021 if (scanf("%c%*c", &resp)) { 1022 if (resp == 'y' || resp == 'Y') { 1023 cmd_chosen++; 1024 cli_context->action = CLI_INIT_BS; 1025 } else { 1026 if (cli_context->cli_mode == CLI_MODE_CMD) { 1027 spdk_app_stop(0); 1028 return false; 1029 } 1030 } 1031 } 1032 } else { 1033 cmd_chosen++; 1034 cli_context->action = CLI_INIT_BS; 1035 } 1036 break; 1037 case 'r': 1038 if (argv[optind] != NULL) { 1039 cmd_chosen++; 1040 cli_context->action = CLI_REM_XATTR; 1041 cli_context->blobid = atoll(optarg); 1042 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1043 } else { 1044 usage(cli_context, "ERROR: missing parameter.\n"); 1045 } 1046 break; 1047 case 'l': 1048 if (strcmp("bdevs", optarg) == 0) { 1049 cmd_chosen++; 1050 cli_context->action = CLI_LIST_BDEVS; 1051 } else if (strcmp("blobs", optarg) == 0) { 1052 cmd_chosen++; 1053 cli_context->action = CLI_LIST_BLOBS; 1054 } else { 1055 usage(cli_context, "ERROR: invalid option for list\n"); 1056 } 1057 break; 1058 case 'm': 1059 if (argv[optind] != NULL) { 1060 cmd_chosen++; 1061 cli_context->action = CLI_IMPORT_BLOB; 1062 cli_context->blobid = atoll(optarg); 1063 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1064 } else { 1065 usage(cli_context, "ERROR: missing parameter.\n"); 1066 } 1067 break; 1068 case 'n': 1069 cli_context->num_clusters = atoi(optarg); 1070 if (cli_context->num_clusters > 0) { 1071 cmd_chosen++; 1072 cli_context->action = CLI_CREATE_BLOB; 1073 } else { 1074 usage(cli_context, "ERROR: invalid option for new\n"); 1075 } 1076 break; 1077 case 'p': 1078 cmd_chosen++; 1079 cli_context->action = CLI_SET_SUPER; 1080 cli_context->superid = atoll(optarg); 1081 break; 1082 case 'S': 1083 if (cli_context->cli_mode == CLI_MODE_CMD) { 1084 cmd_chosen++; 1085 cli_context->cli_mode = CLI_MODE_SHELL; 1086 } 1087 cli_context->action = CLI_NONE; 1088 break; 1089 case 's': 1090 cmd_chosen++; 1091 if (strcmp("bs", optarg) == 0) { 1092 cli_context->action = CLI_SHOW_BS; 1093 } else { 1094 cli_context->action = CLI_SHOW_BLOB; 1095 cli_context->blobid = atoll(optarg); 1096 } 1097 break; 1098 case 'T': 1099 if (cli_context->cli_mode == CLI_MODE_CMD) { 1100 cmd_chosen++; 1101 cli_context->cli_mode = CLI_MODE_SCRIPT; 1102 if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) { 1103 g_script.ignore_errors = true; 1104 } else { 1105 g_script.ignore_errors = false; 1106 } 1107 snprintf(cli_context->script_file, BUFSIZE, "%s", optarg); 1108 } else { 1109 cli_context->action = CLI_NONE; 1110 } 1111 break; 1112 case 'X': 1113 cmd_chosen++; 1114 cli_context->action = CLI_SHELL_EXIT; 1115 break; 1116 case 'x': 1117 if (argv[optind] != NULL || argv[optind + 1] != NULL) { 1118 cmd_chosen++; 1119 cli_context->action = CLI_SET_XATTR; 1120 cli_context->blobid = atoll(optarg); 1121 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1122 snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]); 1123 } else { 1124 usage(cli_context, "ERROR: missing parameter.\n"); 1125 } 1126 break; 1127 default: 1128 usage(cli_context, "ERROR: invalid option\n"); 1129 } 1130 /* only one actual command can be done at a time */ 1131 if (cmd_chosen > 1) { 1132 usage(cli_context, "Error: Please choose only one command\n"); 1133 } 1134 } 1135 1136 if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) { 1137 usage(cli_context, "Error: Please choose a command.\n"); 1138 } 1139 1140 /* 1141 * We don't check the local boolean because in some modes it will have been set 1142 * on and earlier command. 1143 */ 1144 if (strcmp(cli_context->bdev_name, "") == 0) { 1145 usage(cli_context, "Error: -b option is required.\n"); 1146 cmd_chosen = 0; 1147 } 1148 1149 /* in shell mode we'll call getopt multiple times so need to reset its index */ 1150 optind = 0; 1151 return (cmd_chosen == 1); 1152 } 1153 1154 /* 1155 * In script mode, we parsed a script file at startup and saved off a bunch of cmd 1156 * lines that we now parse with each run of cli_start so we us the same cmd parser 1157 * as cmd and shell modes. 1158 */ 1159 static bool 1160 line_parser(struct cli_context_t *cli_context) 1161 { 1162 bool cmd_chosen; 1163 char *tok = NULL; 1164 int blob_num = 0; 1165 int start_idx = cli_context->argc; 1166 int i; 1167 1168 printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]); 1169 tok = strtok(g_script.cmdline[g_script.cmdline_idx], " "); 1170 while (tok != NULL) { 1171 /* 1172 * We support one replaceable token right now, a $Bn 1173 * represents the blobid that was created in position n 1174 * so fish this out now and use it here. 1175 */ 1176 cli_context->argv[cli_context->argc] = strdup(tok); 1177 if (tok[0] == '$' && tok[1] == 'B') { 1178 tok += 2; 1179 blob_num = atoi(tok); 1180 if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) { 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 } else { 1194 printf("ERROR: Invalid token or exceeded max blobs of %d\n", 1195 MAX_SCRIPT_BLOBS); 1196 } 1197 } 1198 cli_context->argc++; 1199 tok = strtok(NULL, " "); 1200 } 1201 1202 /* call parse cmd line with user input as args */ 1203 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1204 1205 /* free strdup memory and reset arg count for next shell interaction */ 1206 for (i = start_idx; i < cli_context->argc; i++) { 1207 free(cli_context->argv[i]); 1208 cli_context->argv[i] = NULL; 1209 } 1210 cli_context->argc = 1; 1211 1212 g_script.cmdline_idx++; 1213 assert(g_script.cmdline_idx < MAX_SCRIPT_LINES); 1214 1215 if (cmd_chosen == false) { 1216 printf("ERROR: Invalid script line starting with: %s\n\n", 1217 g_script.cmdline[g_script.cmdline_idx - 1]); 1218 if (g_script.ignore_errors == false) { 1219 printf("** Aborting **\n"); 1220 cli_context->action = CLI_SHELL_EXIT; 1221 cmd_chosen = true; 1222 unload_bs(cli_context, "", 0); 1223 } else { 1224 printf("** Skipping **\n"); 1225 } 1226 } 1227 1228 return cmd_chosen; 1229 } 1230 1231 /* 1232 * For script mode, we read a series of commands from a text file and store them 1233 * in a global struct. That, along with the cli_mode that tells us we're in 1234 * script mode is what feeds the rest of the app in the same way as is it were 1235 * getting commands from shell mode. 1236 */ 1237 static void 1238 parse_script(struct cli_context_t *cli_context) 1239 { 1240 FILE *fp = NULL; 1241 size_t bufsize = BUFSIZE; 1242 int64_t bytes_in = 0; 1243 int i = 0; 1244 1245 /* initialize global script values */ 1246 for (i = 0; i < MAX_SCRIPT_BLOBS; i++) { 1247 g_script.blobid[i] = 0; 1248 } 1249 g_script.blobid_idx = 0; 1250 g_script.cmdline_idx = 0; 1251 i = 0; 1252 1253 fp = fopen(cli_context->script_file, "r"); 1254 if (fp == NULL) { 1255 printf("ERROR: unable to open script: %s\n", 1256 cli_context->script_file); 1257 cli_cleanup(cli_context); 1258 exit(-1); 1259 } 1260 1261 do { 1262 bytes_in = getline(&g_script.cmdline[i], &bufsize, fp); 1263 if (bytes_in > 0) { 1264 /* replace newline with null */ 1265 spdk_str_chomp(g_script.cmdline[i]); 1266 1267 /* ignore comments */ 1268 if (g_script.cmdline[i][0] != '#') { 1269 i++; 1270 } 1271 } 1272 } while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1); 1273 fclose(fp); 1274 1275 /* add an exit cmd in case they didn't */ 1276 g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE); 1277 if (g_script.cmdline[i] == NULL) { 1278 int j; 1279 1280 for (j = 0; j < i; j++) { 1281 free(g_script.cmdline[j]); 1282 g_script.cmdline[j] = NULL; 1283 } 1284 unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0); 1285 } 1286 snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X"); 1287 g_script.max_index = i; 1288 } 1289 1290 /* 1291 * Provides for a shell interface as opposed to one shot command line. 1292 */ 1293 static bool 1294 cli_shell(void *arg1, void *arg2) 1295 { 1296 struct cli_context_t *cli_context = arg1; 1297 char *line = NULL; 1298 ssize_t buf_size = 0; 1299 ssize_t bytes_in = 0; 1300 ssize_t tok_len = 0; 1301 char *tok = NULL; 1302 bool cmd_chosen = false; 1303 int start_idx = cli_context->argc; 1304 int i; 1305 1306 printf("blob> "); 1307 bytes_in = getline(&line, &buf_size, stdin); 1308 1309 /* parse input and update cli_context so we can use common option parser */ 1310 if (bytes_in > 0) { 1311 tok = strtok(line, " "); 1312 } 1313 while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) { 1314 cli_context->argv[cli_context->argc] = strdup(tok); 1315 tok_len = strlen(tok); 1316 cli_context->argc++; 1317 tok = strtok(NULL, " "); 1318 } 1319 1320 /* replace newline on last arg with null */ 1321 if (tok_len) { 1322 spdk_str_chomp(cli_context->argv[cli_context->argc - 1]); 1323 } 1324 1325 /* call parse cmd line with user input as args */ 1326 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1327 1328 /* free strdup mem & reset arg count for next shell interaction */ 1329 for (i = start_idx; i < cli_context->argc; i++) { 1330 free(cli_context->argv[i]); 1331 cli_context->argv[i] = NULL; 1332 } 1333 cli_context->argc = 1; 1334 1335 free(line); 1336 1337 return cmd_chosen; 1338 } 1339 1340 /* 1341 * This is the function we pass into the SPDK framework that gets 1342 * called first. 1343 */ 1344 static void 1345 cli_start(void *arg1, void *arg2) 1346 { 1347 struct cli_context_t *cli_context = arg1; 1348 1349 /* 1350 * If we're in script mode, we already have a list of commands so 1351 * just need to pull them out one at a time and process them. 1352 */ 1353 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1354 while (line_parser(cli_context) == false); 1355 } 1356 1357 /* 1358 * The initial cmd line options are parsed once before this function is 1359 * called so if there is no action, we're in shell mode and will loop 1360 * here until a a valid option is parsed and returned. 1361 */ 1362 if (cli_context->action == CLI_NONE) { 1363 while (cli_shell(cli_context, NULL) == false); 1364 } 1365 1366 /* Decide what to do next based on cmd line parsing. */ 1367 switch (cli_context->action) { 1368 case CLI_SET_SUPER: 1369 case CLI_SHOW_BS: 1370 case CLI_CREATE_BLOB: 1371 case CLI_SET_XATTR: 1372 case CLI_REM_XATTR: 1373 case CLI_SHOW_BLOB: 1374 case CLI_LIST_BLOBS: 1375 case CLI_DUMP_BLOB: 1376 case CLI_IMPORT_BLOB: 1377 case CLI_FILL: 1378 load_bs(cli_context); 1379 break; 1380 case CLI_INIT_BS: 1381 init_bs(cli_context); 1382 break; 1383 case CLI_LIST_BDEVS: 1384 list_bdevs(cli_context); 1385 break; 1386 case CLI_SHELL_EXIT: 1387 /* 1388 * Because shell mode reuses cmd mode functions, the blobstore 1389 * is loaded/unloaded with every action so we just need to 1390 * stop the framework. For this app there's no need to optimize 1391 * and keep the blobstore open while the app is in shell mode. 1392 */ 1393 spdk_app_stop(0); 1394 break; 1395 case CLI_HELP: 1396 usage(cli_context, ""); 1397 unload_complete(cli_context, 0); 1398 break; 1399 default: 1400 /* should never get here */ 1401 exit(-1); 1402 break; 1403 } 1404 } 1405 1406 int 1407 main(int argc, char **argv) 1408 { 1409 struct spdk_app_opts opts = {}; 1410 struct cli_context_t *cli_context = NULL; 1411 bool cmd_chosen; 1412 int rc = 0; 1413 1414 if (argc < 2) { 1415 usage(cli_context, "ERROR: Invalid option\n"); 1416 exit(-1); 1417 } 1418 1419 cli_context = calloc(1, sizeof(struct cli_context_t)); 1420 if (cli_context == NULL) { 1421 printf("ERROR: could not allocate context structure\n"); 1422 exit(-1); 1423 } 1424 1425 /* default to CMD mode until we've parsed the first parms */ 1426 cli_context->cli_mode = CLI_MODE_CMD; 1427 cli_context->argv[0] = strdup(argv[0]); 1428 cli_context->argc = 1; 1429 1430 /* parse command line */ 1431 cmd_chosen = cmd_parser(argc, argv, cli_context); 1432 free(cli_context->argv[0]); 1433 cli_context->argv[0] = NULL; 1434 if (cmd_chosen == false) { 1435 cli_cleanup(cli_context); 1436 exit(-1); 1437 } 1438 1439 /* after displaying help, just exit */ 1440 if (cli_context->action == CLI_HELP) { 1441 usage(cli_context, ""); 1442 cli_cleanup(cli_context); 1443 exit(-1); 1444 } 1445 1446 /* if they don't supply a conf name, use the default */ 1447 if (!cli_context->config_file) { 1448 cli_context->config_file = program_conf; 1449 } 1450 1451 /* if the config file doesn't exist, tell them how to make one */ 1452 if (access(cli_context->config_file, F_OK) == -1) { 1453 printf("Error: No config file found.\n"); 1454 printf("To create a config file named 'blobcli.conf' for your NVMe device:\n"); 1455 printf(" <path to spdk>/scripts/gen_nvme.sh > blobcli.conf\n"); 1456 printf("and then re-run the cli tool.\n"); 1457 exit(-1); 1458 } 1459 1460 /* 1461 * For script mode we keep a bunch of stuff in a global since 1462 * none if it is passed back and forth to SPDK. 1463 */ 1464 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1465 /* 1466 * Now we'll build up the global which will direct this run of the app 1467 * as it will have a list (g_script) of all of the commands line by 1468 * line as if they were typed in on the shell at cmd line. 1469 */ 1470 parse_script(cli_context); 1471 } 1472 1473 /* Set default values in opts struct along with name and conf file. */ 1474 spdk_app_opts_init(&opts); 1475 opts.name = "blobcli"; 1476 opts.config_file = cli_context->config_file; 1477 1478 cli_context->app_started = true; 1479 rc = spdk_app_start(&opts, cli_start, cli_context, NULL); 1480 if (rc) { 1481 printf("ERROR!\n"); 1482 } 1483 1484 /* Free up memory that we allocated */ 1485 cli_cleanup(cli_context); 1486 1487 /* Gracefully close out all of the SPDK subsystems. */ 1488 spdk_app_fini(); 1489 return rc; 1490 } 1491