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